Deployment Guide
Overview
This guide covers deployment strategies, environment setup, and best practices for deploying the www application in different environments.
Deployment Architecture
Infrastructure Components
- Web Server: Node.js application server
- Reverse Proxy: Nginx for load balancing and SSL termination
- Database/Cache: Redis for session storage and caching
- Container Platform: Docker and Docker Compose
Environment Structure
┌─ Production ─┐ ┌─ Staging ─┐ ┌─ Development ─┐
│ Live App │ │ Testing │ │ Local │
│ Real Data │ │ Sandbox │ │ Hot Reload │
│ SSL/CDN │ │ Testing │ │ Debug Mode │
└──────────────┘ └───────────┘ └──────────────┘
Docker Deployment
Docker Configuration
Located in docker/www/Dockerfile:
FROM node:20.11.1-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Build assets
RUN npm run build
# Expose port
EXPOSE 8081
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8081/health || exit 1
# Start application
CMD ["npm", "start"]
Docker Compose Setup
# docker-compose.yml
version: '3.8'
services:
www:
build:
context: ./www
dockerfile: ../docker/www/Dockerfile
ports:
- "8081:8081"
environment:
- NODE_ENV=production
- REDIS_URL=redis://redis:6379
depends_on:
- redis
restart: unless-stopped
volumes:
- ./logs:/app/logs
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- www
restart: unless-stopped
volumes:
redis_data:
Environment Setup
Production Environment
Prerequisites
- Docker and Docker Compose
- SSL certificates
- Domain name configuration
- Environment variables configured
Deployment Steps
# 1. Clone repository
git clone <repository-url>
cd tunnelflight
# 2. Set up environment variables
cp .env.example .env.production
# Edit .env.production with production values
# 3. Build and start services
docker-compose -f docker-compose.prod.yml up -d
# 4. Verify deployment
docker-compose ps
curl -f http://localhost:8081/health
Staging Environment
Configuration
# Staging-specific environment
NODE_ENV=staging
APP_PORT=8082
REDIS_URL=redis://staging-redis:6379
# Build and deploy
docker-compose -f docker-compose.staging.yml up -d
Development Environment
Local Setup
# Install dependencies
cd www
npm install
# Set up local environment
cp .env.example .env
# Edit .env with development values
# Start development server
npm run dev
Nginx Configuration
Reverse Proxy Setup
# /etc/nginx/sites-available/tunnelflight
server {
listen 80;
listen 443 ssl http2;
server_name yourdomain.com;
# SSL Configuration
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security Headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
# Gzip Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Static Assets
location /css/ {
alias /var/www/tunnelflight/public/css/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /js/ {
alias /var/www/tunnelflight/public/js/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /img/ {
alias /var/www/tunnelflight/public/img/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Proxy to Node.js App
location / {
proxy_pass http://www:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Health Check
location /health {
proxy_pass http://www:8081/health;
access_log off;
}
}
Environment Variables
Production Variables
# Application
NODE_ENV=production
APP_PORT=8081
IS_PRODUCTION=true
# Database/Cache
REDIS_URL=redis://prod-redis:6379
REDIS_PASSWORD=secure_redis_password
# Authentication
JWT_SECRET=your_production_jwt_secret
GOOGLE_CLIENT_ID=prod_google_client_id
GOOGLE_CLIENT_SECRET=prod_google_client_secret
# External Services
INFISICAL_CLIENT_ID=prod_infisical_client_id
INFISICAL_CLIENT_SECRET=prod_infisical_client_secret
INFISICAL_PROJECT_ID=prod_project_id
# Monitoring
LOG_LEVEL=info
SENTRY_DSN=your_sentry_dsn
Staging Variables
# Application
NODE_ENV=staging
APP_PORT=8082
# Use staging credentials and endpoints
GOOGLE_CLIENT_ID=staging_google_client_id
API_BASE_URL=https://staging-api.yourdomain.com
CI/CD Pipeline
GitHub Actions Example
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20.11.1'
cache: 'npm'
cache-dependency-path: www/package-lock.json
- name: Install dependencies
run: |
cd www
npm ci
- name: Run tests
run: |
cd www
npm test
- name: Build assets
run: |
cd www
npm run build
- name: Build Docker image
run: docker build -t tunnelflight-www:${{ github.sha }} -f docker/www/Dockerfile ./www
- name: Deploy to production
run: |
# Deploy logic (e.g., push to registry, update services)
echo "Deploying to production..."
Health Checks and Monitoring
Application Health Check
// Health check endpoint
app.get('/health', (req, res) => {
const health = {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
version: process.env.npm_package_version,
environment: process.env.NODE_ENV
};
// Check Redis connection
redisClient.ping((err, result) => {
if (err) {
health.status = 'unhealthy';
health.redis = 'disconnected';
} else {
health.redis = 'connected';
}
const statusCode = health.status === 'healthy' ? 200 : 503;
res.status(statusCode).json(health);
});
});
Docker Health Check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8081/health || exit 1
Monitoring Integration
- Application Monitoring: Sentry for error tracking
- Performance Monitoring: APM tools integration
- Log Aggregation: Centralized logging setup
- Uptime Monitoring: External service monitoring
SSL/TLS Configuration
Certificate Management
# Let's Encrypt with Certbot
certbot --nginx -d yourdomain.com
# Or manual certificate installation
sudo cp cert.pem /etc/nginx/ssl/
sudo cp private.key /etc/nginx/ssl/
SSL Best Practices
- Use TLS 1.2 or higher
- Strong cipher suites
- HSTS headers
- Certificate auto-renewal
Database Migrations and Seeding
Redis Data Management
# Backup Redis data
redis-cli --rdb /backup/dump.rdb
# Restore Redis data
redis-cli --pipe < /backup/restore.redis
Data Seeding
# Seed initial data
npm run seed:production
Troubleshooting
Common Deployment Issues
Container Won't Start
# Check container logs
docker-compose logs www
# Verify environment variables
docker-compose exec www env | grep NODE_ENV
Redis Connection Issues
# Test Redis connectivity
docker-compose exec www redis-cli -h redis ping
# Check Redis logs
docker-compose logs redis
Nginx Configuration Issues
# Test Nginx configuration
docker-compose exec nginx nginx -t
# Reload Nginx configuration
docker-compose exec nginx nginx -s reload
Performance Optimization
Application Optimization
- Enable gzip compression
- Optimize asset loading
- Implement caching strategies
- Monitor memory usage
Database Optimization
- Redis memory optimization
- Connection pooling
- Query optimization
- Index management
Rollback Procedures
Application Rollback
# Tag current version before deployment
docker tag tunnelflight-www:latest tunnelflight-www:backup
# Rollback to previous version
docker-compose down
docker-compose up -d tunnelflight-www:previous-version
Database Rollback
# Restore Redis from backup
redis-cli flushall
redis-cli --pipe < /backup/previous-state.redis