Skip to main content
โšก Calmops

Deployment and Production Readiness

Deployment and Production Readiness

Deploying applications to production requires careful planning and execution. This article covers deployment best practices.

Introduction

Production readiness provides:

  • Reliable deployments
  • Scalability
  • Performance optimization
  • Security hardening
  • Monitoring and alerting

Understanding production readiness helps you:

  • Deploy applications safely
  • Scale applications
  • Monitor performance
  • Handle failures
  • Maintain uptime

Environment Configuration

Environment Variables

// โœ… Good: Install dotenv
// npm install dotenv

require('dotenv').config();

// .env
NODE_ENV=production
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your-secret-key
LOG_LEVEL=info

// app.js
const port = process.env.PORT || 3000;
const nodeEnv = process.env.NODE_ENV || 'development';
const dbUrl = process.env.DATABASE_URL;

console.log(`Environment: ${nodeEnv}`);
console.log(`Port: ${port}`);

// โœ… Good: Validate environment variables
const requiredEnvVars = [
  'DATABASE_URL',
  'JWT_SECRET',
  'NODE_ENV'
];

requiredEnvVars.forEach(envVar => {
  if (!process.env[envVar]) {
    throw new Error(`Missing required environment variable: ${envVar}`);
  }
});

// โœ… Good: Environment-specific configuration
const config = {
  development: {
    debug: true,
    logLevel: 'debug'
  },
  production: {
    debug: false,
    logLevel: 'error'
  }
};

const appConfig = config[process.env.NODE_ENV];

Configuration Management

// config/index.js
module.exports = {
  app: {
    port: process.env.PORT || 3000,
    env: process.env.NODE_ENV || 'development'
  },
  database: {
    url: process.env.DATABASE_URL,
    pool: {
      min: 2,
      max: 10
    }
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: '1h'
  },
  logging: {
    level: process.env.LOG_LEVEL || 'info',
    format: 'json'
  },
  security: {
    corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:3000',
    rateLimit: {
      windowMs: 15 * 60 * 1000,
      max: 100
    }
  }
};

// app.js
const config = require('./config');

const app = express();
const port = config.app.port;

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

Security Hardening

Security Middleware

// โœ… Good: Install security packages
// npm install helmet express-rate-limit cors

const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const cors = require('cors');

const app = express();

// โœ… Good: Use Helmet for security headers
app.use(helmet());

// โœ… Good: Enable CORS
app.use(cors({
  origin: process.env.CORS_ORIGIN,
  credentials: true
}));

// โœ… Good: Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  message: 'Too many requests'
});

app.use('/api/', limiter);

// โœ… Good: Disable powered by header
app.disable('x-powered-by');

// โœ… Good: Validate input
const { body, validationResult } = require('express-validator');

app.post('/api/users', [
  body('email').isEmail(),
  body('password').isLength({ min: 8 })
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  // Process request
});

HTTPS and SSL

// โœ… Good: Use HTTPS in production
const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync(process.env.SSL_KEY_PATH),
  cert: fs.readFileSync(process.env.SSL_CERT_PATH)
};

https.createServer(options, app).listen(443, () => {
  console.log('HTTPS server running on port 443');
});

// โœ… Good: Redirect HTTP to HTTPS
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https') {
    res.redirect(`https://${req.header('host')}${req.url}`);
  } else {
    next();
  }
});

Performance Optimization

Caching

// โœ… Good: Install caching packages
// npm install redis

const redis = require('redis');
const client = redis.createClient({
  host: process.env.REDIS_HOST,
  port: process.env.REDIS_PORT
});

// โœ… Good: Cache middleware
const cacheMiddleware = (duration) => {
  return (req, res, next) => {
    if (req.method !== 'GET') {
      return next();
    }

    const key = `cache:${req.originalUrl}`;

    client.get(key, (err, data) => {
      if (data) {
        res.json(JSON.parse(data));
      } else {
        res.sendResponse = res.json;
        res.json = (body) => {
          client.setex(key, duration, JSON.stringify(body));
          res.sendResponse(body);
        };
        next();
      }
    });
  };
};

app.get('/api/users', cacheMiddleware(300), (req, res) => {
  res.json([{ id: 1, name: 'John' }]);
});

Compression

// โœ… Good: Install compression
// npm install compression

const compression = require('compression');

app.use(compression());

// โœ… Good: Custom compression settings
app.use(compression({
  level: 6,
  threshold: 1024
}));

Monitoring and Logging

Application Monitoring

// โœ… Good: Install monitoring packages
// npm install pm2

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'app',
    script: './app.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production'
    },
    error_file: './logs/error.log',
    out_file: './logs/out.log',
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z'
  }]
};

// Start with: pm2 start ecosystem.config.js
// Monitor: pm2 monit
// Logs: pm2 logs

Health Checks

// โœ… Good: Health check endpoint
app.get('/health', (req, res) => {
  const health = {
    status: 'ok',
    timestamp: new Date(),
    uptime: process.uptime(),
    memory: process.memoryUsage()
  };
  res.json(health);
});

// โœ… Good: Readiness check
app.get('/ready', async (req, res) => {
  try {
    await checkDatabaseConnection();
    res.json({ ready: true });
  } catch (err) {
    res.status(503).json({ ready: false, error: err.message });
  }
});

Deployment Strategies

Docker Deployment

# โœ… Good: Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD ["node", "app.js"]

# โœ… Good: .dockerignore
node_modules
npm-debug.log
.git
.env

Docker Compose

# โœ… Good: docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DATABASE_URL: mongodb://db:27017/myapp
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: mongo:5
    volumes:
      - mongo-data:/data/db
    restart: unless-stopped

volumes:
  mongo-data:

Cloud Deployment

# โœ… Good: Deploy to Heroku
heroku create my-app
git push heroku main
heroku logs --tail

# โœ… Good: Deploy to AWS
aws elasticbeanstalk create-environment \
  --application-name my-app \
  --environment-name production

# โœ… Good: Deploy to Google Cloud
gcloud app deploy

# โœ… Good: Deploy to Azure
az webapp up --name my-app

Scaling

Horizontal Scaling

// โœ… Good: Cluster mode with PM2
const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  const numCPUs = os.cpus().length;

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork();
  });
} else {
  app.listen(3000, () => {
    console.log(`Worker ${process.pid} started`);
  });
}

Load Balancing

# โœ… Good: Nginx configuration
upstream app {
  server localhost:3000;
  server localhost:3001;
  server localhost:3002;
}

server {
  listen 80;
  server_name example.com;

  location / {
    proxy_pass http://app;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

Best Practices

  1. Use environment variables:

    // โœ… Good: Environment variables
    const port = process.env.PORT || 3000;
    
    // โŒ Bad: Hardcoded values
    const port = 3000;
    
  2. Implement health checks:

    // โœ… Good: Health check
    app.get('/health', (req, res) => {
      res.json({ status: 'ok' });
    });
    
    // โŒ Bad: No health check
    
  3. Use process managers:

    # โœ… Good: PM2
    pm2 start app.js
    
    # โŒ Bad: Direct node
    node app.js
    

Summary

Production readiness is essential. Key takeaways:

  • Configure environment variables
  • Harden security
  • Optimize performance
  • Monitor applications
  • Implement health checks
  • Use process managers
  • Scale horizontally
  • Deploy safely

Next Steps

Comments