Production Deployment GuideΒΆ
Complete guide for deploying the CyferWall Engage Backend to production environments.
π Deployment OverviewΒΆ
Deployment TargetsΒΆ
- Vercel (Primary) - Serverless deployment
- Docker - Containerized deployment
- Traditional VPS - Virtual private server
Deployment PipelineΒΆ
graph LR
A[Code Push] --> B[GitHub Actions]
B --> C[Tests & Linting]
C --> D[Build & Bundle]
D --> E[Deploy to Vercel]
E --> F[Health Check]
F --> G[Notification] ποΈ Vercel Deployment (Recommended)ΒΆ
Vercel ConfigurationΒΆ
// vercel.json
{
"version": 2,
"name": "cyferwall-engage-backend",
"builds": [
{
"src": "dist/server.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/api/v1/(.*)",
"dest": "/dist/server.js"
},
{
"src": "/(.*)",
"dest": "/dist/server.js"
}
],
"env": {
"NODE_ENV": "production"
},
"functions": {
"dist/server.js": {
"maxDuration": 30
}
},
"regions": ["iad1", "sfo1"],
"includedFiles": [
"dist/**",
"node_modules/@prisma/client/**",
"node_modules/.prisma/**",
"prisma/**",
"templates/**",
"package.json"
]
}
Environment VariablesΒΆ
# Production environment variables
NODE_ENV=production
PORT=3000
API_VERSION=v1
# Database
DATABASE_URL=postgresql://user:pass@host:port/db
# Authentication
JWT_SECRET=your-super-secure-jwt-secret-256-bits
AUTH_COOKIE_NAME=cyferwall-auth
AUTH_HEADER_NAME=x-cyferwall-auth
# External Services
RESEND_API_KEY=re_your_resend_api_key
RESEND_SENDER_ADDRESS=noreply@cyferwall.com
# Monitoring
DD_API_KEY=your_datadog_api_key
DD_ENV=production
DD_SERVICE=engage-backend
DD_VERSION=1.0.0
# Security
BCRYPT_SALT_ROUNDS=12
CORS_ORIGINS=https://cyferwall.com,https://*.cyferwall.com
Deployment CommandsΒΆ
# Install Vercel CLI
npm install -g vercel
# Login to Vercel
vercel login
# Deploy to production
vercel --prod
# Set environment variables
vercel env add NODE_ENV
vercel env add DATABASE_URL
vercel env add JWT_SECRET
π³ Docker DeploymentΒΆ
DockerfileΒΆ
# Multi-stage build
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY prisma ./prisma/
# Install dependencies
RUN npm i --only=production
# Generate Prisma client
RUN npx prisma generate
# Copy source code
COPY . .
# Build application
RUN npm run build
# Production image
FROM node:18-alpine AS production
WORKDIR /app
# Install dumb-init for signal handling
RUN apk add --no-cache dumb-init
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodeuser -u 1001
# Copy built application
COPY --from=builder --chown=nodeuser:nodejs /app/dist ./dist
COPY --from=builder --chown=nodeuser:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodeuser:nodejs /app/package.json ./
COPY --from=builder --chown=nodeuser:nodejs /app/prisma ./prisma
COPY --from=builder --chown=nodeuser:nodejs /app/templates ./templates
USER nodeuser
EXPOSE 3000
ENV NODE_ENV=production
ENV PORT=3000
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]
Docker Compose (Development)ΒΆ
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/engage
- JWT_SECRET=development-secret
depends_on:
- db
volumes:
- ./templates:/app/templates
db:
image: postgres:14-alpine
environment:
POSTGRES_DB: engage
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Docker Deployment CommandsΒΆ
# Build image
docker build -t cyferwall/engage-backend:latest .
# Run container
docker run -d \
--name engage-backend \
-p 3000:3000 \
-e DATABASE_URL="postgresql://..." \
-e JWT_SECRET="..." \
cyferwall/engage-backend:latest
# Docker Compose
docker-compose up -d
# View logs
docker logs engage-backend -f
# Stop container
docker stop engage-backend
π¦ Build ProcessΒΆ
TypeScript CompilationΒΆ
# Build for production
npm run build
# Output structure
dist/
βββ server.js # Main application entry
βββ routers/ # Route handlers
βββ util/ # Utilities and helpers
βββ (common)/ # Common modules
βββ *.js.map # Source maps
Build ConfigurationΒΆ
// tsconfig.json (production)
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": false,
"sourceMap": true,
"removeComments": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests"]
}
Build OptimizationΒΆ
# Production build with optimizations
NODE_ENV=production npm run build
# Bundle analysis
npx webpack-bundle-analyzer dist/stats.json
# Tree shaking check
npx ts-unused-exports tsconfig.json
π§ Production ConfigurationΒΆ
Performance SettingsΒΆ
// Production server configuration
const productionConfig = {
// Express settings
trustProxy: true,
compression: true,
etag: false,
// Database connection pool
database: {
connectionLimit: 50,
idleTimeout: 30000,
acquireTimeout: 60000
},
// Caching
cache: {
redis: process.env.REDIS_URL,
ttl: 300 // 5 minutes
},
// Rate limiting
rateLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 1000 // requests per window
}
}
Security HardeningΒΆ
// Security middleware
import helmet from 'helmet'
import rateLimit from 'express-rate-limit'
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}))
app.use(rateLimit({
windowMs: 15 * 60 * 1000,
max: 1000,
message: 'Too many requests from this IP'
}))
π Monitoring & ObservabilityΒΆ
Health ChecksΒΆ
// Comprehensive health check
app.get('/health', async (req, res) => {
const checks = {
status: 'healthy',
timestamp: new Date().toISOString(),
version: process.env.npm_package_version,
environment: process.env.NODE_ENV,
uptime: process.uptime(),
checks: {
database: await checkDatabase(),
memory: checkMemory(),
disk: await checkDisk(),
externalServices: await checkExternalServices()
}
}
const isHealthy = Object.values(checks.checks).every(check => check.status === 'healthy')
res.status(isHealthy ? 200 : 503).json(checks)
})
Logging ConfigurationΒΆ
// Production logging with Winston
import winston from 'winston'
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: 'engage-backend',
environment: process.env.NODE_ENV
},
transports: [
new winston.transports.File({
filename: 'logs/error.log',
level: 'error'
}),
new winston.transports.File({
filename: 'logs/combined.log'
}),
new winston.transports.Console({
format: winston.format.simple()
})
]
})
DataDog APMΒΆ
// DataDog tracing
import tracer from 'dd-trace'
tracer.init({
service: 'engage-backend',
env: process.env.NODE_ENV,
version: process.env.npm_package_version,
logInjection: true,
runtimeMetrics: true
})
// Custom metrics
import StatsD from 'hot-shots'
const dogstatsd = new StatsD({
hostname: 'localhost',
port: 8125,
prefix: 'engage.backend.'
})
// Track business metrics
dogstatsd.increment('cases.created')
dogstatsd.histogram('notifications.sent', duration)
π CI/CD PipelineΒΆ
GitHub Actions WorkflowΒΆ
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm i
- name: Run tests
run: npm test
- name: Run linting
run: npm run lint
- name: Type checking
run: npm run type-check
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build application
run: |
npm i
npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
- name: Run deployment tests
run: |
npm run test:deployment
- name: Notify team
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
ποΈ Database MigrationΒΆ
Production Migration StrategyΒΆ
# Safe migration process
# 1. Backup database
pg_dump $DATABASE_URL > backup_$(date +%Y%m%d).sql
# 2. Run migrations
npx prisma migrate deploy
# 3. Verify migration
npx prisma migrate status
# 4. Generate new client
npx prisma generate
Zero-Downtime DeploymentΒΆ
// Blue-green deployment strategy
const deploymentStrategy = {
// 1. Deploy to staging environment
staging: 'https://staging-api.cyferwall.com',
// 2. Run smoke tests
smokeTests: [
'GET /health',
'POST /common/support/case',
'GET /metrics'
],
// 3. Switch traffic gradually
trafficSplit: {
blue: 0, // Old version
green: 100 // New version
}
}
π‘οΈ Security ChecklistΒΆ
Pre-Deployment SecurityΒΆ
- Environment variables secured
- SSL/TLS certificates installed
- Rate limiting configured
- Input validation enabled
- SQL injection protection
- XSS prevention headers
- CORS properly configured
- Secrets rotation implemented
Post-Deployment VerificationΒΆ
- Health checks passing
- SSL certificate valid
- API endpoints responding
- Database connections working
- Notifications sending
- Monitoring alerts configured
- Backup systems operational
π¨ Rollback ProceduresΒΆ
Automatic RollbackΒΆ
# Vercel automatic rollback
vercel rollback --timeout 300s
# Docker rollback
docker tag cyferwall/engage-backend:v1.0.0 cyferwall/engage-backend:latest
docker service update --image cyferwall/engage-backend:latest engage-backend
Manual RollbackΒΆ
# 1. Stop current deployment
vercel --prod --yes --confirm rollback
# 2. Restore database if needed
pg_restore -d $DATABASE_URL backup_20250123.sql
# 3. Verify rollback
curl https://api.cyferwall.com/health
π Support & TroubleshootingΒΆ
Production Support ContactsΒΆ
- DevOps Team: devops@cyferwall.com
- On-Call Engineer: +1-555-ONCALL
- Slack: #production-support
Common IssuesΒΆ
- Memory leaks: Monitor with
process.memoryUsage() - Database connections: Check connection pool status
- API timeouts: Review response times and optimize
- SSL certificate expiry: Set up auto-renewal
Emergency ProceduresΒΆ
- Service outage: Follow incident response playbook
- Data breach: Execute security incident protocol
- Performance degradation: Scale resources and investigate
π Production deployment requires careful planning and monitoring. Always test thoroughly before deploying to production.