NestJS course Β· Module 9: Deployment and Infrastructure

PROJECT: Full deployment of a NestJS application to production

4 min read
In this lesson2

Legate! Your application runs on your laptop, but Consul Caesar.js asks the questions that come up in every real deployment: what happens when the server goes dark at three in the morning? Who will notice that the database is slowing down? How do you know yesterday's backup can be restored? The Consul entrusts you with the greatest challenge - a complete deployment of the Roman legion to production. It is the true test of the skills you have gained during our NestJS campaign.

Project Specification

Your task is to create a complete deployment system for the Legionary Legion API application. Every part builds on the lessons of this and earlier modules, so before you start, go back to them like to staff maps. The hours given are an approximate amount of work.

1. Infrastructure (4-5 hours)

  • Docker and Docker Compose configuration
  • Kubernetes/Helm charts setup
  • CI/CD pipeline configuration
  • Monitoring and logging

Start with an image built in a multi-stage build and a Compose file that brings up the application together with MongoDB and Redis. In Kubernetes describe the replicas, the rolling update strategy and the readiness and liveness probes. The pipeline should run the tests, build the image and deploy it only after the tests are green.

2. Security (2-3 hours)

  • SSL/TLS implementation
  • Security headers and rate limiting
  • Secrets management
  • Vulnerability scanning

You already know HTTPS, Helmet headers and throttler limits. Keep secrets outside the repository and the image, and let the application refuse to start when one of them is missing. Add a vulnerability scanner, e.g. Trivy, to the pipeline, so that an image with a critical flaw never reaches production.

3. Performance (2-3 hours)

  • Load balancing and auto-scaling
  • Caching strategy
  • Database optimization
  • CDN setup

Measure before you optimize: a load test result before and after your changes is the best evidence. Match indexes to the most frequent queries, add a Redis cache with well-thought-out invalidation, and cache headers for the CDN. Autoscaling adds replicas based on metrics such as CPU, so the application must be stateless: sessions and caches live in Redis, not in process memory.

4. Monitoring (2-3 hours)

  • Health checks
  • Metrics collection
  • Alerting system
  • Log aggregation

Split health endpoints into liveness and readiness, expose metrics for Prometheus, and write logs in JSON with a request identifier. An alert should wake someone up only when something needs to be done - an alarm that is too loud quickly stops being heard.

5. Backup & Recovery (1-2 hours)

  • Automated backups
  • Disaster recovery plan
  • Data integrity checks

Run backups automatically, set your RTO and RPO, then carry out one full restore on a fresh environment and write down how long it took.

Evaluation Criteria

  • Completeness - all components work together
  • Security - proper safeguards on every layer, from TLS to secrets
  • Performance - the application handles production traffic, as a load test confirms
  • Monitoring - full observability: metrics, logs and alerts
  • Documentation - deployment and recovery runbooks

The mentor will assess the project from the repository, so the README must contain the start-up commands, an architecture description and the results of the restore test. I recommend writing the runbook alongside the configuration not at the end - whatever you do not write down straight away will be mere guesswork at three in the morning.

Good luck, legate! Your legion is counting on you, and you will send the finished system to your mentor at the end of the module.

Code for this lesson: deployment-project/docker-compose.yml
1# PROJECT: Full NestJS Application Deployment to Production
2
3version: '3.8'
4
5services:
6  # === API NestJS ===
7  api:
8    build:
9      context: .
10      dockerfile: Dockerfile
11    container_name: roman-api
12    restart: unless-stopped
13    ports:
14      - "4000:4000"
15    environment:
16      NODE_ENV: production
17      PORT: 4000
18      MONGO_URI: mongodb://mongo:27017/roman_empire
19      REDIS_HOST: redis
20      REDIS_PORT: 6379
21      JWT_SECRET: ${JWT_SECRET}
22      ALLOWED_ORIGINS: https://roman-empire.com
23    depends_on:
24      mongo:
25        condition: service_healthy
26      redis:
27        condition: service_healthy
28    healthcheck:
29      test: ["CMD", "node", "-e",
30        "require('http').get('http://localhost:4000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"]
31      interval: 30s
32      timeout: 10s
33      retries: 3
34      start_period: 40s
35    networks:
36      - roman-network
37
38  # === MongoDB ===
39  mongo:
40    image: mongo:7
41    container_name: roman-mongodb
42    restart: unless-stopped
43    volumes:
44      - mongo-data:/data/db
45      - ./scripts/backup.sh:/scripts/backup.sh
46    environment:
47      MONGO_INITDB_DATABASE: roman_empire
48    healthcheck:
49      test: ["CMD", "mongosh", "--eval", "db.runCommand('ping').ok"]
50      interval: 10s
51      timeout: 5s
52      retries: 5
53    networks:
54      - roman-network
55
56  # === Redis Cache ===
57  redis:
58    image: redis:7-alpine
59    container_name: roman-redis
60    restart: unless-stopped
61    command: redis-server --appendonly yes
62    volumes:
63      - redis-data:/data
64    healthcheck:
65      test: ["CMD", "redis-cli", "ping"]
66      interval: 10s
67      timeout: 3s
68      retries: 3
69    networks:
70      - roman-network
71
72  # === Nginx Reverse Proxy ===
73  nginx:
74    image: nginx:alpine
75    container_name: roman-nginx
76    restart: unless-stopped
77    ports:
78      - "80:80"
79    volumes:
80      - ./nginx.conf:/etc/nginx/nginx.conf:ro
81    depends_on:
82      - api
83    networks:
84      - roman-network
85
86# Volumes
87volumes:
88  mongo-data:
89    driver: local
90  redis-data:
91    driver: local
92
93# Network
94networks:
95  roman-network:
96    driver: bridge
97
98# Commands:
99# docker-compose up -d        # Run
100# docker-compose logs -f api  # API logs
101# docker-compose down         # Stop
102# docker-compose ps           # Status
103

Spotted a mistake in this lesson?

Useful articles