Skip to main content

Deployment and Production Readiness

Created: May 8, 2026 Larry Qu 5 min read

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;
    ```javascript
    
  2. Implement health checks:
    // ✅ Good: Health check
    app.get('/health', (req, res) => {
      res.json({ status: 'ok' });
    });
    
    // ❌ Bad: No health check
    ```javascript
    
  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

Resources

Comments

Share this article

Scan to read on mobile