Skip to content

Deployment Guide

Guide for building and deploying IONFLOW Frontend to various environments.

Terminal window
# Build for production
pnpm build
# Preview the build locally
pnpm preview

The build outputs to dist/:

dist/
├── assets/
│ ├── index-[hash].js # Main bundle
│ ├── index-[hash].css # Styles
│ └── [chunks]-[hash].js # Code-split chunks
├── static/
│ └── img/ # Static images
├── index.html
└── favicon.ico
Terminal window
# Development
pnpm build --mode development
# Staging
pnpm build --mode staging
# Production
pnpm build --mode production

Variables are replaced at build time:

.env.production
VITE_API_URL=https://api.ionflow.com/api
VITE_KEYCLOAK_URL=https://auth.ionflow.com
VITE_ENABLE_DEV_TOOLS=false

For runtime configuration, use a config endpoint or inject variables:

// Load config at runtime
const config = await fetch('/config.json').then(r => r.json());

The build output is static and can be hosted on:

  • Nginx
  • Apache
  • AWS S3 + CloudFront
  • Netlify
  • Vercel
  • GitHub Pages
server {
listen 80;
server_name app.ionflow.com;
root /var/www/ionflow;
index index.html;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# Cache static assets
location /assets {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# API proxy
location /api {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# Dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install
COPY . .
ARG VITE_API_URL
ARG VITE_KEYCLOAK_URL
RUN pnpm build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
docker-compose.yml
version: '3.8'
services:
frontend:
build:
context: .
args:
VITE_API_URL: ${VITE_API_URL}
VITE_KEYCLOAK_URL: ${VITE_KEYCLOAK_URL}
ports:
- "80:80"
environment:
- NODE_ENV=production
k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ionflow-frontend
spec:
replicas: 3
selector:
matchLabels:
app: ionflow-frontend
template:
metadata:
labels:
app: ionflow-frontend
spec:
containers:
- name: frontend
image: ionflow/frontend:latest
ports:
- containerPort: 80
resources:
limits:
cpu: "100m"
memory: "128Mi"
---
apiVersion: v1
kind: Service
metadata:
name: ionflow-frontend
spec:
selector:
app: ionflow-frontend
ports:
- port: 80
targetPort: 80
.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 20
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test:unit
- name: Build
env:
VITE_API_URL: ${{ secrets.API_URL }}
VITE_KEYCLOAK_URL: ${{ secrets.KEYCLOAK_URL }}
run: pnpm build
- name: Deploy to S3
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- run: aws s3 sync dist/ s3://ionflow-frontend --delete
- name: Invalidate CloudFront
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DIST_ID }} --paths "/*"
vite.config.ts
export default defineConfig({
build: {
// Enable minification
minify: 'terser',
// Chunk splitting
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
primevue: ['primevue'],
},
},
},
// Source maps for production debugging
sourcemap: true,
},
});
ResourceCache DurationStrategy
HTMLNo cacheAlways fetch
JS/CSS (hashed)1 yearImmutable
Images1 weekStale-while-revalidate
Fonts1 yearImmutable
# CloudFront behaviors
/assets/* - Cache 1 year, gzip
/static/* - Cache 1 week, gzip
/index.html - No cache
/* - Forward to origin
location /health {
return 200 'OK';
add_header Content-Type text/plain;
}
main.ts
import * as Sentry from '@sentry/vue';
if (import.meta.env.PROD) {
Sentry.init({
app,
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
});
}
  1. Deploy new version to “green” environment
  2. Test green environment
  3. Switch traffic from “blue” to “green”
  4. Keep blue available for rollback
Terminal window
# Tag release
git tag v1.2.3
git push origin v1.2.3
# Build specific version
docker build -t ionflow/frontend:v1.2.3 .
  • All tests pass
  • Build succeeds
  • Environment variables configured
  • Version tagged
  • Application loads correctly
  • Authentication works
  • API calls succeed
  • No console errors
  • Monitoring alerts configured