Node.js Production Checklist
20 essential steps to ensure your Node.js application is production-ready, secure, and reliable.
Deploying a Node.js application to production is more than just uploading your code to a server. It requires careful attention to security, performance, reliability, and monitoring. This comprehensive checklist covers 20 essential steps across six critical categories to ensure your application is production-ready.
Security (4 items)
Install Helmet for HTTP headers
Helmet.js secures your Express/Node.js app by setting HTTP response headers.
npm install helmet import helmet from 'helmet'; app.use(helmet());
Implement rate limiting
Protect against brute force and DDoS attacks with express-rate-limit.
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);Configure CORS properly
Restrict cross-origin requests to prevent unauthorized access.
import cors from 'cors';
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(','),
credentials: true
}));Validate and sanitize input
Prevent injection attacks with express-validator.
import { body, validationResult } from 'express-validator';
app.post('/user',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(400).json({ errors });
}
);Performance (4 items)
Enable cluster mode
Use all CPU cores with PM2 cluster mode or Node.js cluster module.
pm2 start app.js -i max --name "myapp"
Implement caching strategies
Use Redis for session management, caching, and rate limiting.
import redis from 'redis';
const client = redis.createClient();
// Cache expensive DB queries
const cachedUser = await client.get(`user:${id}`);
if (!cachedUser) {
const user = await db.getUser(id);
await client.setEx(`user:${id}`, 3600, JSON.stringify(user));
}Enable gzip compression
Reduce response size for faster transmission.
import compression from 'compression'; app.use(compression());
Optimize database queries
Add indexes, use query optimization, avoid N+1 problems.
// Bad: N+1 queries
const users = await User.find();
for (let user of users) {
user.posts = await Post.find({ userId: user.id });
}
// Good: Use .populate() or JOIN
const users = await User.find().populate('posts');Reliability (4 items)
Use PM2 for process management
Automatically restart crashed processes and manage app lifecycle.
npm install -g pm2 pm2 start app.js --name "myapp" pm2 startup pm2 save
Implement health check endpoints
Allow load balancers to verify app is alive and healthy.
app.get('/health', (req, res) => {
res.status(200).json({
status: 'ok',
uptime: process.uptime(),
timestamp: new Date().toISOString()
});
});Handle graceful shutdown
Close connections cleanly on SIGTERM signals.
let server = app.listen(3000);
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing server');
server.close(() => {
console.log('HTTP server closed');
process.exit(0);
});
// Force exit after 10 seconds
setTimeout(() => process.exit(1), 10000);
});Set resource limits
Configure memory and file descriptor limits.
// PM2 ecosystem.config.js
module.exports = {
apps: [{
name: 'app',
script: './app.js',
max_memory_restart: '500M',
node_args: '--max-old-space-size=512'
}]
};Logging (2 items)
Use structured logging
Log JSON for easier parsing and analysis with Winston or Pino.
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
logger.info('User logged in', { userId: 123, timestamp: new Date() });Configure log rotation
Prevent logs from consuming unlimited disk space.
import 'winston-daily-rotate-file';
const transport = new winston.transports.DailyRotateFile({
filename: 'application-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d'
});
logger.add(transport);Monitoring (3 items)
Monitor uptime
Use Datadog, New Relic, or Prometheus to track application availability.
Track memory and CPU usage
Set alerts for resource thresholds to prevent out-of-memory crashes.
Monitor error rates
Use error tracking (Sentry, Bugsnag) to catch issues in production.
Infrastructure (3 items)
Set up Nginx reverse proxy
Load balance between multiple Node.js instances and handle static files.
upstream nodejs {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://nodejs;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
}Enable SSL/TLS certificates
Use Let's Encrypt for free HTTPS, auto-renew with certbot.
sudo certbot certonly --nginx -d example.com sudo certbot renew --dry-run # Test auto-renewal
Configure firewall rules
Use UFW to restrict inbound traffic to necessary ports.
sudo ufw allow 22/tcp # SSH sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS sudo ufw enable
How DeployWise Handles Infrastructure
DeployWise automates the entire infrastructure checklist for you. Our platform automatically configures Nginx reverse proxies, SSL certificates, firewall rules, and monitors all critical metrics. Focus on your application code while we handle production reliability.
Start with DeployWiseRelated Guides
Ready to deploy with confidence?
Let DeployWise handle your Node.js production infrastructure, so you can focus on building great applications.