docker.recipes

Production Node.js Stack

advanced

Production Node.js with Nginx, PM2, PostgreSQL, Redis, and BullMQ for job queues

Overview

Node.js is a JavaScript runtime built on Chrome's V8 engine that enables server-side JavaScript execution, revolutionizing web development by allowing developers to use a single language across their entire stack. PM2 is a production-grade process manager for Node.js applications that provides clustering, monitoring, and automatic restarts, making it the de facto standard for running Node.js in production environments. This production stack combines Node.js with PM2 for robust application management, NGINX as a high-performance reverse proxy, PostgreSQL for reliable ACID-compliant data storage, and Redis powering both caching and BullMQ job queuing. The architecture creates a scalable, fault-tolerant system where NGINX handles SSL termination and load balancing, PM2 manages Node.js processes in cluster mode, PostgreSQL ensures data integrity, and Redis enables fast session storage and background job processing. This combination is ideal for high-traffic web applications, API services, and enterprise systems that require both performance and reliability. Startups scaling beyond basic deployments, enterprises modernizing legacy systems, and development teams building real-time applications will find this stack provides the production-grade infrastructure needed to handle demanding workloads while maintaining operational simplicity.

Key Features

  • PM2 cluster mode with automatic load balancing across CPU cores
  • NGINX reverse proxy with SSL/TLS termination and HTTP/2 support
  • PostgreSQL ACID compliance with advanced transaction support and JSON/JSONB capabilities
  • Redis sub-millisecond caching with rich data structures and pub/sub messaging
  • BullMQ job queue processing with Redis-backed persistence and retry mechanisms
  • Event-driven Node.js architecture with non-blocking I/O operations
  • PM2 automatic restart and crash recovery with zero-downtime deployments
  • PostgreSQL full-text search and complex query optimization for analytics

Common Use Cases

  • 1E-commerce platforms requiring inventory management, order processing, and payment workflows
  • 2SaaS applications with user authentication, subscription billing, and background email processing
  • 3Real-time collaboration tools needing WebSocket connections and live data synchronization
  • 4Content management systems with image processing, search indexing, and publication workflows
  • 5API backends for mobile applications handling user sessions and push notifications
  • 6Financial applications requiring transaction integrity and audit trails with background reporting
  • 7Social media platforms with user-generated content, real-time feeds, and notification systems

Prerequisites

  • Minimum 2GB RAM (PostgreSQL requires 256MB+, Redis 512MB+, Node.js cluster 1GB+)
  • Docker and Docker Compose installed with support for multi-service orchestration
  • Basic understanding of Node.js ecosystem, npm/yarn, and JavaScript async patterns
  • Familiarity with SQL queries and PostgreSQL database administration
  • Knowledge of NGINX configuration syntax for reverse proxy and SSL setup
  • Understanding of process management concepts and PM2 ecosystem configuration

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 nginx:
3 image: nginx:alpine
4 container_name: node-nginx
5 restart: unless-stopped
6 ports:
7 - "${NGINX_PORT:-80}:80"
8 volumes:
9 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
10 - ./public:/app/public:ro
11 depends_on:
12 - app
13
14 app:
15 build:
16 context: .
17 dockerfile: Dockerfile
18 container_name: node-app
19 restart: unless-stopped
20 command: pm2-runtime ecosystem.config.js
21 volumes:
22 - ./:/app
23 - /app/node_modules
24 environment:
25 - NODE_ENV=production
26 - PORT=3000
27 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
28 - REDIS_URL=redis://redis:6379
29 - SESSION_SECRET=${SESSION_SECRET}
30 depends_on:
31 - db
32 - redis
33
34 worker:
35 build:
36 context: .
37 dockerfile: Dockerfile
38 container_name: node-worker
39 restart: unless-stopped
40 command: node dist/worker.js
41 volumes:
42 - ./:/app
43 - /app/node_modules
44 environment:
45 - NODE_ENV=production
46 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
47 - REDIS_URL=redis://redis:6379
48 depends_on:
49 - db
50 - redis
51
52 db:
53 image: postgres:16-alpine
54 container_name: node-db
55 restart: unless-stopped
56 environment:
57 - POSTGRES_USER=${DB_USER}
58 - POSTGRES_PASSWORD=${DB_PASSWORD}
59 - POSTGRES_DB=${DB_NAME}
60 volumes:
61 - postgres_data:/var/lib/postgresql/data
62
63 redis:
64 image: redis:7-alpine
65 container_name: node-redis
66 restart: unless-stopped
67 volumes:
68 - redis_data:/data
69
70volumes:
71 postgres_data:
72 redis_data:

.env Template

.env
1# Node.js Production Stack
2NGINX_PORT=80
3
4# App
5SESSION_SECRET=your-session-secret
6
7# Database
8DB_USER=node
9DB_PASSWORD=node_password
10DB_NAME=node_app

Usage Notes

  1. 1Create Dockerfile with Node.js and PM2
  2. 2Create ecosystem.config.js for PM2 cluster mode
  3. 3Run migrations with your ORM (Prisma, TypeORM, etc.)
  4. 4Worker processes BullMQ jobs from Redis
  5. 5PM2 handles process management and clustering
  6. 6Scale workers: docker compose up -d --scale worker=3

Individual Services(5 services)

Copy individual services to mix and match with your existing compose files.

nginx
nginx:
  image: nginx:alpine
  container_name: node-nginx
  restart: unless-stopped
  ports:
    - ${NGINX_PORT:-80}:80
  volumes:
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    - ./public:/app/public:ro
  depends_on:
    - app
app
app:
  build:
    context: .
    dockerfile: Dockerfile
  container_name: node-app
  restart: unless-stopped
  command: pm2-runtime ecosystem.config.js
  volumes:
    - ./:/app
    - /app/node_modules
  environment:
    - NODE_ENV=production
    - PORT=3000
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    - REDIS_URL=redis://redis:6379
    - SESSION_SECRET=${SESSION_SECRET}
  depends_on:
    - db
    - redis
worker
worker:
  build:
    context: .
    dockerfile: Dockerfile
  container_name: node-worker
  restart: unless-stopped
  command: node dist/worker.js
  volumes:
    - ./:/app
    - /app/node_modules
  environment:
    - NODE_ENV=production
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    - REDIS_URL=redis://redis:6379
  depends_on:
    - db
    - redis
db
db:
  image: postgres:16-alpine
  container_name: node-db
  restart: unless-stopped
  environment:
    - POSTGRES_USER=${DB_USER}
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=${DB_NAME}
  volumes:
    - postgres_data:/var/lib/postgresql/data
redis
redis:
  image: redis:7-alpine
  container_name: node-redis
  restart: unless-stopped
  volumes:
    - redis_data:/data

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 nginx:
5 image: nginx:alpine
6 container_name: node-nginx
7 restart: unless-stopped
8 ports:
9 - "${NGINX_PORT:-80}:80"
10 volumes:
11 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
12 - ./public:/app/public:ro
13 depends_on:
14 - app
15
16 app:
17 build:
18 context: .
19 dockerfile: Dockerfile
20 container_name: node-app
21 restart: unless-stopped
22 command: pm2-runtime ecosystem.config.js
23 volumes:
24 - ./:/app
25 - /app/node_modules
26 environment:
27 - NODE_ENV=production
28 - PORT=3000
29 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
30 - REDIS_URL=redis://redis:6379
31 - SESSION_SECRET=${SESSION_SECRET}
32 depends_on:
33 - db
34 - redis
35
36 worker:
37 build:
38 context: .
39 dockerfile: Dockerfile
40 container_name: node-worker
41 restart: unless-stopped
42 command: node dist/worker.js
43 volumes:
44 - ./:/app
45 - /app/node_modules
46 environment:
47 - NODE_ENV=production
48 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
49 - REDIS_URL=redis://redis:6379
50 depends_on:
51 - db
52 - redis
53
54 db:
55 image: postgres:16-alpine
56 container_name: node-db
57 restart: unless-stopped
58 environment:
59 - POSTGRES_USER=${DB_USER}
60 - POSTGRES_PASSWORD=${DB_PASSWORD}
61 - POSTGRES_DB=${DB_NAME}
62 volumes:
63 - postgres_data:/var/lib/postgresql/data
64
65 redis:
66 image: redis:7-alpine
67 container_name: node-redis
68 restart: unless-stopped
69 volumes:
70 - redis_data:/data
71
72volumes:
73 postgres_data:
74 redis_data:
75EOF
76
77# 2. Create the .env file
78cat > .env << 'EOF'
79# Node.js Production Stack
80NGINX_PORT=80
81
82# App
83SESSION_SECRET=your-session-secret
84
85# Database
86DB_USER=node
87DB_PASSWORD=node_password
88DB_NAME=node_app
89EOF
90
91# 3. Start the services
92docker compose up -d
93
94# 4. View logs
95docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/production-nodejs-stack/run | bash

Troubleshooting

  • PM2 processes not starting: Check ecosystem.config.js syntax and ensure Node.js dependencies are installed with npm ci
  • NGINX 502 Bad Gateway: Verify Node.js app is running on port 3000 and container networking allows nginx to reach app service
  • PostgreSQL connection refused: Ensure DATABASE_URL format is correct and database container is healthy before app starts
  • Redis connection timeout: Check REDIS_URL configuration and verify Redis container is accessible on port 6379
  • BullMQ jobs stuck in waiting: Confirm worker container is running and can connect to Redis, check worker.js for error handling
  • High memory usage: Monitor PM2 processes with pm2 monit and adjust cluster instances in ecosystem.config.js based on available RAM

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space