Project Deployment
Now that MegaShop has been developed, Charlie needs to deploy it to the production environment. This isn't just a simple npm run build—it requires Docker orchestration for the entire suite of services, a CI/CD automation pipeline, runtime monitoring and error tracing, and a comprehensive deployment checklist. Deploy once, run stably for the long term.
1. What You'll Learn
- Production Build Optimization: nuxt build analysis + bundle optimization + environment variable security
- Docker Compose Orchestration: Nuxt + PostgreSQL + Redis + Nginx
- CI/CD Pipeline: Automated Testing with GitHub Actions + Deployment Across Multiple Environments
- Monitoring and Logging: Sentry Error Tracking + API Logging + Performance Metrics
- Deployment Checklist: SSL/CDN/Migration/Cache Preheating/Rollback Plan
2. A True Story of an Architect
(1) Pain Point: Crashes Immediately Upon Launch
Charlie's first deployment of MegaShop—he forgot to run the database migrations, missed configuring the environment variables, didn't start Redis, and the SSL certificate had expired. Five minutes after going live, Alice noticed a 500 error, and it took Bob an hour to manually roll back the changes.
(2) Solutions for Production-Level Deployment Systems
A comprehensive deployment system: automated CI/CD + Docker Compose + monitoring + deployment checklist, with validation at every step and automatic rollback in case of errors.
(3) Benefits: Automation + Observability
Once the code is pushed, it deploys automatically; Sentry captures errors in real time; Prometheus monitors performance metrics; and Alice will never encounter an undetected 500 error again.
3. Production Build Optimization
(1) Analysis of Construction Products
(1) ▶ Example: Build Analysis and Optimization
# Build for production
npm run build
# Analyze bundle
npx nuxi analyze
Output:
# Command executed successfully
(2) ▶ Example: Environment Variable Security Check
// server/utils/env-check.ts
export default defineEventHandler((event) => {
if (getRequestURL(event).pathname !== '/api/health') return
const config = useRuntimeConfig()
const required = ['databaseUrl', 'jwtAccessSecret', 'jwtRefreshSecret']
const missing = required.filter(key => !config[key as keyof typeof config])
if (missing.length > 0 && process.env.NODE_ENV === 'production') {
console.error(`Missing required env vars: ${missing.join(', ')}`)
}
return {
status: missing.length === 0 ? 'ok' : 'degraded',
timestamp: Date.now(),
version: process.env.APP_VERSION || 'unknown'
}
})
Output:
// Execution Successful
(2) Develop an Optimization Checklist
| Check Item | Requirement | Verification Method |
|---|---|---|
| JS Bundle gzip | < 200KB | nuxi analyze |
| CSS Extraction | ✅ Separate File | Build Output Check |
| WebP Images | ✅ Automatic Conversion | NuxtImg Validation |
| Environment Variables | No Hard-Coded Keys | grep Check |
| Tree Shaking | No Unused Code | Bundle Analysis |
| Source Map | Production Disabled | nuxt.config.ts |
4. Docker Compose Orchestration
(1) Production Deployment Process
flowchart TB
A[Git Push] --> B[GitHub Actions CI]
B --> C{Tests Pass?}
C -->|No| D[Notify Team + Block]
C -->|Yes| E[Build Docker Image]
E --> F[Push to Registry]
F --> G[Deploy to Staging]
G --> H{Smoke Test?}
H -->|No| I[Rollback Staging]
H -->|Yes| J[Deploy to Production]
J --> K{Health Check?}
K -->|No| L[Auto Rollback]
K -->|Yes| M[Done ✅]
(1) ▶ Example: Production-Grade Docker Compose
# docker-compose.prod.yml
version: '3.8'
services:
web:
image: ghcr.io/megashop/megashop:latest
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://megashop:${DB_PASSWORD}@db:5432/megashop
- REDIS_URL=redis://redis:6379
- JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET}
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
- DEPLOY_TARGET=node-server
depends_on:
db: { condition: service_healthy }
redis: { condition: service_started }
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
deploy:
resources:
limits: { memory: 1G, cpus: '1.0' }
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=megashop
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=megashop
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U megashop"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.prod.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- web
restart: unless-stopped
volumes:
postgres_data:
redis_data:
Output:
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
5. CI/CD Pipeline
(1) ▶ Example: Complete Production Deployment Workflow
# .github/workflows/deploy-production.yml
name: Deploy Production
on:
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: megashop/megashop
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env: { POSTGRES_USER: test, POSTGRES_PASSWORD: test, POSTGRES_DB: megashop_test }
ports: ['5432:5432']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npx prisma migrate deploy
env: { DATABASE_URL: postgresql://test:test@localhost:5432/megashop_test }
- run: npm run lint
- run: npm run typecheck
- run: npm run test:coverage
build-push:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GHCR_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
deploy:
needs: build-push
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.PROD_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
script: |
cd /opt/megashop
docker pull ghcr.io/megashop/megashop:latest
docker compose -f docker-compose.prod.yml up -d --no-build
docker compose exec web npx prisma migrate deploy
echo "Deploy completed at $(date)"
- name: Health check
run: |
for i in {1..15}; do
if curl -sf https://megashop.com/api/health; then
echo "Health check passed"
exit 0
fi
echo "Waiting for health check... ($i/15)"
sleep 5
done
echo "Health check failed!"
exit 1
- name: Rollback on failure
if: failure()
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.PROD_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
script: |
cd /opt/megashop
docker compose down
docker tag ghcr.io/megashop/megashop:previous ghcr.io/megashop/megashop:latest
docker compose -f docker-compose.prod.yml up -d --no-build
echo "Rollback completed at $(date)"
Output:
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
6. Monitoring and Logging
(1) ▶ Example: Sentry Error Tracking
// plugins/sentry.client.ts
import * as Sentry from '@sentry/vue'
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
Sentry.init({
dsn: config.public.sentryDsn,
environment: config.public.environment,
release: config.public.version,
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(nuxtApp.$router)
})
],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0
})
return {
provide: {
sentry: Sentry
}
}
})
Output:
// Execution Successful
(2) ▶ Example: Server-side logs
// server/middleware/logging.ts
export default defineEventHandler((event) => {
const start = Date.now()
const method = getMethod(event)
const url = getRequestURL(event)
event.node.res.on('finish', () => {
const duration = Date.now() - start
const status = event.node.res.statusCode
// Structured log
const logEntry = {
method, url: url.pathname,
status, duration,
userId: event.context.user?.id || null,
timestamp: new Date().toISOString()
}
if (status >= 500) {
console.error(JSON.stringify(logEntry))
} else if (duration > 1000) {
console.warn(JSON.stringify({ ...logEntry, alert: 'slow_request' }))
} else {
console.log(JSON.stringify(logEntry))
}
})
})
Output:
// Execution Successful
(3) ▶ Example: Health Check API
// server/api/health.get.ts
export default defineEventHandler(async () => {
const checks: Record<string, string> = {}
// Check database
try {
await prisma.$queryRaw`SELECT 1`
checks.database = 'ok'
} catch {
checks.database = 'error'
}
// Check Redis
try {
const storage = useStorage('products')
await storage.setItem('health-check', 'ok', { ttl: 10 })
checks.redis = 'ok'
} catch {
checks.redis = 'error'
}
const allOk = Object.values(checks).every(v => v === 'ok')
setHeader(useEvent(), 'cache-control', 'no-store')
return {
status: allOk ? 'ok' : 'degraded',
checks,
version: process.env.APP_VERSION || 'unknown',
uptime: process.uptime(),
timestamp: new Date().toISOString()
}
})
Output:
// Execution Successful
7. Deployment Checklist
(1) Production Launch Inspection
| # | Check Item | Action | Verification Method |
|---|---|---|---|
| 1 | SSL Certificate | Configure Let's Encrypt | curl -v https://megashop.com |
| 2 | Environment Variables | All keys have been configured | /api/health check |
| 3 | Database Migration | prisma migrate deploy | Product Query Validation |
| 4 | Redis Connection | Start and Connect | Check /api/health |
| 5 | CDN Configuration | Cloudflare/CloudFront | Static Resource Cache Hits |
| 6 | Cache Preheating | First Visit to Popular Products Page | curl Home Page + List Page |
| 7 | Health Check | /api/health returns "ok" | Automated Verification |
| 8 | Sentry Initialization | Error Tracking Enabled | Trigger Test Error |
| 9 | Sitemap Generation | /sitemap.xml is accessible | Google Search Console |
| 10 | Rollback Plan | Old Image Available | Document Rollback Steps |
(2) Rollback Plan
| Step | Action | Command |
|---|---|---|
| 1 | Stop the current service | docker compose down |
| 2 | Restore Old Image | docker tag xxx:previous xxx:latest |
| 3 | Launch an older version | docker compose up -d --no-build |
| 4 | Health Check | curl /api/health |
| 5 | Notify the Team | Slack/Email Notifications |
8. Comprehensive Example: MegaShop Deployment Commands
# ============================================
# MegaShop Production Deployment
# Complete deployment + monitoring setup
# ============================================
# 1. Set up server (first time only)
ssh prod-server
mkdir -p /opt/megashop/ssl
# Copy SSL certificates
# Copy .env with production secrets
# 2. Initial deployment
cd /opt/megashop
git clone https://github.com/megashop/megashop.git .
docker compose -f docker-compose.prod.yml up -d
# 3. Database setup
docker compose exec web npx prisma migrate deploy
docker compose exec web npx prisma db seed
# 4. Verify
curl -f https://megashop.com/api/health
curl -f https://megashop.com/sitemap.xml
# 5. Cache warmup
curl https://megashop.com/
curl https://megashop.com/products
curl https://megashop.com/products/1
# 6. Monitor
docker compose logs -f web
# Check Sentry dashboard
# Check health endpoint periodically
❓ FAQ
docker pull followed by docker-compose up -d --no-build. Use blue-green deployment for zero downtime.docker compose logs. For production environments, we recommend ELK (Elasticsearch + Logstash + Kibana) or Loki + Grafana. In its early stages, MegaShop used structured console.log entries combined with Sentry.prisma migrate deploy only applies validated migration files. If a migration fails, fix the migration file and redeploy it. Never use prisma migrate reset in production (it will result in data loss).📖 Summary
- Production build: bundle analysis + environment variable security + Source Maps disabled
- Docker Compose Orchestration: Full Stack of Nuxt + PostgreSQL + Redis + Nginx Services
- CI/CD: GitHub Actions—automated testing, building, deployment, health checks, and automatic rollbacks
- Monitoring: Sentry error tracking + structured logging + health check API
- 10-item deployment checklist: SSL, environment variables, migration, Redis, CDN, cache preheating, rollback plan
📝 Exercises
- Basic Exercise (Difficulty: ⭐): Deploy MegaShop to a production environment (Docker Compose) and verify that /api/health returns "ok"
- Advanced Exercise (Difficulty: ⭐⭐): Set up GitHub Actions CI/CD to automatically deploy upon a push to the
mainbranch, including health checks and automatic rollbacks - Challenge (Difficulty: ⭐⭐⭐): Build a complete operations system—Sentry error tracking + structured logging + Prometheus metrics + automated validation of deployment checklists
---|



