Deployment

This guide covers deploying IOServer applications to production environments with best practices for security, performance, and reliability.

Production Checklist

✅ Essential Steps

  • Set NODE_ENV=production

  • Configure proper logging levels

  • Enable HTTPS/TLS

  • Set up reverse proxy (Nginx/Apache)

  • Configure CORS properly

  • Set up monitoring and health checks

  • Configure error reporting

  • Set up backup strategies

  • Configure rate limiting

  • Enable process management (PM2)

Environment Configuration

Environment Variables

Create a .env file for environment-specific configuration:

# Server Configuration
NODE_ENV=production
HOST=0.0.0.0
PORT=3000
LOG_LEVEL=INFO

# Security
CORS_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
JWT_SECRET=your-super-secret-jwt-key
SESSION_SECRET=your-session-secret

# Database
DATABASE_URL=postgresql://user:password@localhost:5432/ioserver
REDIS_URL=redis://localhost:6379

# Monitoring
SENTRY_DSN=your-sentry-dsn
NEW_RELIC_LICENSE_KEY=your-newrelic-key

# SSL/TLS
SSL_CERT_PATH=/path/to/cert.pem
SSL_KEY_PATH=/path/to/key.pem

Production Server Configuration

import { IOServer } from 'ioserver';
import * as dotenv from 'dotenv';

dotenv.config();

const server = new IOServer({
  host: process.env.HOST || '0.0.0.0',
  port: parseInt(process.env.PORT || '3000'),
  verbose: (process.env.LOG_LEVEL as LogLevel) || 'INFO',
  cors: {
    origin: process.env.CORS_ORIGINS?.split(',') || false,
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization'],
  },
  mode: ['websocket', 'polling'],
});

// Production error handling
process.on('uncaughtException', error => {
  console.error('Uncaught Exception:', error);
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
  process.exit(1);
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  console.log('SIGTERM received, shutting down gracefully');
  await server.stop();
  process.exit(0);
});

process.on('SIGINT', async () => {
  console.log('SIGINT received, shutting down gracefully');
  await server.stop();
  process.exit(0);
});

Docker Deployment

Dockerfile

# Use official Node.js runtime
FROM node:20-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./
COPY pnpm-lock.yaml ./

# Install pnpm
RUN npm install -g pnpm

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source code
COPY . .

# Build application
RUN pnpm run build

# Production image
FROM node:20-alpine AS production

# Create app directory
WORKDIR /app

# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S ioserver -u 1001

# Copy package files
COPY package*.json ./
COPY pnpm-lock.yaml ./

# Install pnpm
RUN npm install -g pnpm

# Install production dependencies only
RUN pnpm install --frozen-lockfile --prod

# Copy built application
COPY --from=builder /app/dist ./dist

# Change ownership to nodejs user
RUN chown -R ioserver:nodejs /app
USER ioserver

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node dist/healthcheck.js || exit 1

# Start application
CMD ["node", "dist/index.js"]

Docker Compose

version: '3.8'

services:
  ioserver:
    build: .
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=production
      - HOST=0.0.0.0
      - PORT=3000
      - DATABASE_URL=postgresql://postgres:password@db:5432/ioserver
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    restart: unless-stopped
    networks:
      - ioserver-network

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ioserver
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - ioserver-network

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    networks:
      - ioserver-network

  nginx:
    image: nginx:alpine
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - ioserver
    networks:
      - ioserver-network

volumes:
  postgres_data:
  redis_data:

networks:
  ioserver-network:
    driver: bridge

Nginx Configuration

nginx.conf

events {
    worker_connections 1024;
}

http {
    upstream ioserver {
        server ioserver:3000;
    }

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

    server {
        listen 80;
        server_name yourdomain.com;
        return 301 https://$server_name$request_uri;
    }

    server {
        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;
        ssl_prefer_server_ciphers on;

        # 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_vary on;
        gzip_types text/plain text/css application/json application/javascript;

        # API routes
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://ioserver;
            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;
        }

        # Socket.IO
        location /socket.io/ {
            proxy_pass http://ioserver;
            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;
        }

        # Static files
        location / {
            proxy_pass http://ioserver;
            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;
        }
    }
}

Process Management with PM2

ecosystem.config.js

module.exports = {
  apps: [
    {
      name: 'ioserver',
      script: 'dist/index.js',
      instances: 'max',
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'development',
        PORT: 3000,
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000,
        LOG_LEVEL: 'INFO',
      },
      error_file: './logs/err.log',
      out_file: './logs/out.log',
      log_file: './logs/combined.log',
      time: true,
      max_memory_restart: '500M',
      node_args: '--max-old-space-size=1024',
    },
  ],
};

PM2 Commands

# Install PM2 globally
npm install -g pm2

# Start application
pm2 start ecosystem.config.js --env production

# Monitor applications
pm2 monit

# View logs
pm2 logs ioserver

# Restart application
pm2 restart ioserver

# Stop application
pm2 stop ioserver

# Save PM2 configuration
pm2 save

# Auto-start on system boot
pm2 startup

Cloud Deployments

AWS ECS with Fargate

task-definition.json

{
  "family": "ioserver",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "ioserver",
      "image": "your-account.dkr.ecr.region.amazonaws.com/ioserver:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "production"
        },
        {
          "name": "PORT",
          "value": "3000"
        }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:database-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/ioserver",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

Heroku Deployment

Procfile

web: node dist/index.js

heroku.yml

build:
  docker:
    web: Dockerfile
run:
  web: node dist/index.js

DigitalOcean App Platform

.do/app.yaml

name: ioserver
services:
  - name: web
    source_dir: /
    github:
      repo: your-username/ioserver-app
      branch: main
    build_command: pnpm install && pnpm run build
    run_command: node dist/index.js
    environment_slug: node-js
    instance_count: 1
    instance_size_slug: basic-xxs
    envs:
      - key: NODE_ENV
        value: 'production'
      - key: PORT
        value: '8080'
      - key: LOG_LEVEL
        value: 'INFO'
    http_port: 8080

Monitoring and Observability

Health Check Endpoint

class HealthController extends BaseController {
  async getHealth(request: any, reply: any) {
    const health = {
      status: 'OK',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      memory: process.memoryUsage(),
      version: process.env.npm_package_version,
    };

    // Check database connectivity
    try {
      await this.appHandle.database.ping();
      health.database = 'connected';
    } catch (error) {
      health.database = 'disconnected';
      health.status = 'ERROR';
    }

    const statusCode = health.status === 'OK' ? 200 : 503;
    reply.status(statusCode).send(health);
  }
}

Logging with Winston

import winston from 'winston';

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console({
      format: winston.format.simple(),
    }),
  ],
});

// Use in IOServer
const server = new IOServer({
  // ... other options
  logger: (level: number, message: string) => {
    const levels = [
      'error',
      'error',
      'error',
      'error',
      'warn',
      'info',
      'info',
      'debug',
    ];
    logger.log(levels[level] || 'info', message);
  },
});

Error Tracking with Sentry

import * as Sentry from '@sentry/node';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
});

// Error handler middleware
class ErrorReportingMiddleware extends BaseMiddleware {
  handle(appHandle: any) {
    return (req: any, reply: any, done: any) => {
      reply.addHook('onError', (request, reply, error, done) => {
        Sentry.captureException(error);
        done();
      });
      done();
    };
  }
}

Security Best Practices

Rate Limiting

import rateLimit from '@fastify/rate-limit';

// Register rate limiting
server.getApp().register(rateLimit, {
  max: 100,
  timeWindow: '1 minute',
  keyGenerator: request => request.ip,
});

Input Validation

import Joi from 'joi';

class ValidationMiddleware extends BaseMiddleware {
  handle(appHandle: any) {
    return (req: any, reply: any, done: any) => {
      const schema = Joi.object({
        username: Joi.string().min(3).max(30).required(),
        email: Joi.string().email().required(),
      });

      const { error } = schema.validate(req.body);
      if (error) {
        return reply.status(400).send({
          statusCode: 400,
          error: 'Validation Error',
          message: error.details[0].message,
        });
      }
      done();
    };
  }
}

HTTPS/TLS Configuration

import fs from 'fs';
import https from 'https';

const server = new IOServer({
  // ... other options
  https: {
    key: fs.readFileSync(process.env.SSL_KEY_PATH),
    cert: fs.readFileSync(process.env.SSL_CERT_PATH),
  },
});

Performance Optimization

Connection Scaling

For high-traffic applications, consider:

  • Load Balancing: Use multiple server instances

  • Session Affinity: Sticky sessions for Socket.IO

  • Redis Adapter: Shared state across instances

  • CDN: Static asset delivery

  • Caching: Redis for frequently accessed data

Resource Monitoring

class PerformanceWatcher extends BaseWatcher {
  async watch() {
    setInterval(() => {
      const memUsage = process.memoryUsage();
      const cpuUsage = process.cpuUsage();

      // Log metrics
      this.appHandle.log(6, `Memory: ${memUsage.heapUsed / 1024 / 1024}MB`);

      // Send to monitoring service
      if (memUsage.heapUsed > 100 * 1024 * 1024) {
        // 100MB
        this.appHandle.log(4, 'High memory usage detected');
      }
    }, 30000);
  }
}

Backup and Recovery

Database Backups

#!/bin/bash
# backup.sh

DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="ioserver_backup_$DATE.sql"

# PostgreSQL backup
pg_dump $DATABASE_URL > $BACKUP_FILE

# Upload to S3
aws s3 cp $BACKUP_FILE s3://your-backup-bucket/

# Clean up local file
rm $BACKUP_FILE

# Keep only last 30 days of backups
aws s3 ls s3://your-backup-bucket/ | while read -r line; do
  createDate=$(echo $line | awk '{print $1" "$2}')
  createDate=$(date -d "$createDate" +%s)
  olderThan=$(date -d '30 days ago' +%s)
  if [[ $createDate -lt $olderThan ]]; then
    fileName=$(echo $line | awk '{print $4}')
    aws s3 rm s3://your-backup-bucket/$fileName
  fi
done

This deployment guide provides comprehensive coverage for taking IOServer applications from development to production with security, scalability, and reliability in mind.