Chapter 9: Deployment & Production
Deploying and running your NestJS app in production requires careful planning. This chapter covers Dockerization, CI/CD, environment management, health checks, logging, monitoring, scaling, and troubleshooting. We’ll walk through practical steps and explain how to make your app production-ready.
9.1 Preparing for Production
Before deploying, ensure your application is production-ready.Production Checklist
Environment Configuration:- Set
NODE_ENV=production - Use environment variables for all secrets
- Remove hardcoded credentials
- Validate all environment variables
- Enable CORS with specific origins
- Set secure HTTP headers (helmet)
- Use HTTPS
- Validate all inputs
- Rate limiting enabled
- Build optimized bundle (
npm run build) - Remove dev dependencies
- Enable compression
- Optimize database queries
- Use connection pooling
- Health checks configured
- Logging set up
- Error tracking (Sentry, etc.)
- Metrics collection
- All tests passing
- Tested in staging environment
- Load testing completed
- Security audit done
Deployment Target Comparison
Decision Framework:
9.2 Dockerizing Your App
Containerization makes deployment consistent and portable. Docker packages your app and dependencies into a single image.Basic Dockerfile
This Dockerfile uses multi-stage builds — a critical Docker optimization. The builder stage has all dev dependencies (TypeScript compiler, etc.) to build the app, but the final image only contains the compiled JavaScript and production dependencies. This typically reduces image size from ~500MB to ~150MB.Optimized Dockerfile
.dockerignore
Docker Compose
- Use multi-stage builds for smaller images
- Keep images minimal (alpine base, no dev dependencies)
- Use
.dockerignoreto exclude unnecessary files - Run as non-root user
- Add health checks
- Use specific version tags
9.3 Environment Variables
Store secrets and configuration in environment variables. Never commit secrets to version control.Using @nestjs/config
Environment Files
Using Config Service
joi) to ensure required environment variables are set and validate their values.
9.4 Health Checks
Health checks help load balancers and orchestrators know if your app is healthy.Installing Terminus
Health Check Controller
Health checks come in two flavors, and understanding the difference is critical for Kubernetes deployments:- Liveness: “Is the process alive?” If this fails, Kubernetes restarts the pod. Keep it simple — do not check external dependencies here, or a database outage will cascade into pod restart storms.
- Readiness: “Can this instance handle traffic?” If this fails, Kubernetes removes the pod from the load balancer but does not restart it. Check database connectivity and other dependencies here.
Custom Health Indicators
9.5 Logging & Monitoring
Proper logging and monitoring are essential for production applications.NestJS Logger
The built-inLogger class is context-aware — passing the class name to the constructor means every log line includes the service name, making it easy to filter logs in production.
Structured Logging
Winston Integration
Error Tracking with Sentry
- Log errors and warnings
- Use structured logs (JSON) for cloud platforms
- Monitor logs and metrics in real time
- Set up alerts for critical errors
- Don’t log sensitive information
- Use log levels appropriately
9.6 CI/CD Pipelines
Automate build, test, and deployment with CI/CD pipelines.GitHub Actions Workflow
GitLab CI
9.7 Kubernetes Deployment
Deploy NestJS applications to Kubernetes for scalability and reliability.Deployment Manifest
Service Manifest
9.8 Scaling & High Availability
Scale your application to handle increased load.Horizontal Scaling
Run multiple instances behind a load balancer. This is the primary scaling strategy for NestJS applications. Key Requirements for Horizontal Scaling: Your NestJS application must be stateless to scale horizontally. This means:- No in-memory sessions (use Redis for sessions)
- No in-memory caches that are not shared (use Redis)
- No file uploads stored on the local filesystem (use S3 or equivalent)
- No WebSocket connections without a Redis adapter (Socket.io with
@socket.io/redis-adapter)
Vertical Scaling
Increase instance size (more CPU/memory). This is a valid first step before going horizontal — a single well-provisioned instance can handle thousands of requests per second. Only scale horizontally when vertical scaling becomes cost-prohibitive or you need fault tolerance.Database Scaling
Caching
9.9 Performance Optimization
Optimize your application for production performance.Enable Compression
Connection Pooling
Query Optimization
- Use indexes on frequently queried columns
- Optimize N+1 queries
- Use select to limit fields
- Implement pagination
9.10 Production Edge Cases
Edge Case 1: Graceful shutdown and in-flight requests When Kubernetes sends SIGTERM to your pod, your NestJS app needs to stop accepting new requests, finish processing in-flight requests, close database connections, and then exit. Without graceful shutdown, users get dropped connections and database transactions may be left in an inconsistent state.onModuleDestroy() and onApplicationShutdown() lifecycle hooks on your providers. The PrismaService example in Chapter 5 uses onModuleDestroy() to close the database connection. Kubernetes gives you 30 seconds by default (configurable via terminationGracePeriodSeconds).
Edge Case 2: Memory leaks from event listeners
If your service subscribes to events (EventEmitter, RxJS subjects, WebSocket events) in the constructor but never unsubscribes, every hot reload in development and every new request-scoped instance in production leaks a listener. Implement OnModuleDestroy and clean up subscriptions.
Edge Case 3: Node.js single-thread and CPU-bound operations
NestJS runs on Node.js, which is single-threaded. If a service method does CPU-intensive work (JSON parsing a 50MB file, image processing, cryptographic operations beyond bcrypt), it blocks the entire event loop and all other requests freeze. Solutions: (1) Use worker threads (worker_threads module); (2) Offload to a separate microservice; (3) Use a job queue (Bull) that processes CPU-intensive work in a separate process.
Edge Case 4: Docker image size and startup time
A NestJS Docker image with all node_modules can be 500MB+. This matters for Kubernetes pod startup time (image pull takes 10-30 seconds) and serverless cold starts. The multi-stage Dockerfile in section 9.2 reduces this to ~150MB. Further optimization: use pnpm with --prod flag or node-prune to strip test files and documentation from node_modules.
9.10 Troubleshooting & Maintenance
Monitor and maintain your production application.Monitoring
- Monitor CPU, memory, and response times
- Track error rates
- Monitor database performance
- Set up alerts for anomalies
Logging
- Centralize logs (ELK, CloudWatch, etc.)
- Search and filter logs
- Set up log retention policies
- Monitor log volumes
Backup & Recovery
- Regular database backups
- Test restore procedures
- Document recovery steps
- Store backups securely
Updates
- Regularly update dependencies
- Test updates in staging
- Use semantic versioning
- Document breaking changes
9.11 Summary
You’ve learned how to deploy and maintain NestJS applications in production: Key Concepts:- Docker: Containerize applications
- CI/CD: Automate deployment
- Health Checks: Monitor application health
- Logging: Track application behavior
- Monitoring: Observe production systems
- Scaling: Handle increased load
- Kubernetes: Orchestrate containers
- Use environment variables for configuration
- Containerize with Docker
- Implement health checks
- Set up proper logging
- Monitor production systems
- Scale horizontally
- Regular backups and updates