Lý thuyết
55 phút
Bài 3/15

Docker Compose cho n8n

Setup n8n production stack với Docker Compose - PostgreSQL, Redis, Nginx reverse proxy, SSL

📦 Docker Compose cho n8n

Container Orchestration

Docker Compose cho phép định nghĩa multi-container applications trong một file. Đây là cách production-ready để deploy n8n.

Docker Compose Basics

What is Docker Compose?

Text
1SINGLE CONTAINER COMPOSE (Multi-Container)
2──────────────── ───────────────────────
3docker run docker-compose.yml
4 --name n8n ┌─────────────────────┐
5 -p 5678:5678 │ services: │
6 -v data:/data │ n8n: │
7 -e VAR=value │ postgres: │
8 n8nio/n8n │ nginx: │
9 │ redis: │
10❌ Hard to manage └─────────────────────┘
11❌ No service linking
12❌ Manual startup order ✅ One command start
13 ✅ Automatic networking
14 ✅ Service dependencies

Basic Structure:

yaml
1# docker-compose.yml
2
3version: "3.8"
4
5services:
6 service1:
7 image: image_name
8 ports:
9 - "host:container"
10 environment:
11 - VAR=value
12 volumes:
13 - volume:/path
14 depends_on:
15 - service2
16
17 service2:
18 image: another_image
19
20volumes:
21 volume:
22
23networks:
24 default:

Minimal n8n Setup

Basic docker-compose.yml:

yaml
1version: "3.8"
2
3services:
4 n8n:
5 image: n8nio/n8n
6 container_name: n8n
7 restart: unless-stopped
8 ports:
9 - "5678:5678"
10 environment:
11 - N8N_BASIC_AUTH_ACTIVE=true
12 - N8N_BASIC_AUTH_USER=admin
13 - N8N_BASIC_AUTH_PASSWORD=changeme
14 - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
15 volumes:
16 - n8n_data:/home/node/.n8n
17
18volumes:
19 n8n_data:

Commands:

Bash
1# Start all services
2docker compose up -d
3
4# View logs
5docker compose logs -f
6
7# Stop
8docker compose down
9
10# Stop and remove volumes (careful!)
11docker compose down -v
12
13# Rebuild after changes
14docker compose up -d --build

Production Setup với PostgreSQL

Why PostgreSQL?

Text
1SQLITE (Default) POSTGRESQL
2──────────────── ──────────
3✅ Zero config ✅ Better performance
4✅ Good for dev ✅ Concurrent access
5❌ Single file ✅ Better backups
6❌ Not scalable ✅ Replication ready
7❌ Locking issues ✅ Production standard

Full Stack docker-compose.yml:

yaml
1version: "3.8"
2
3services:
4 # PostgreSQL Database
5 postgres:
6 image: postgres:15-alpine
7 container_name: n8n-postgres
8 restart: unless-stopped
9 environment:
10 POSTGRES_USER: n8n
11 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
12 POSTGRES_DB: n8n
13 volumes:
14 - postgres_data:/var/lib/postgresql/data
15 healthcheck:
16 test: ["CMD-SHELL", "pg_isready -U n8n"]
17 interval: 10s
18 timeout: 5s
19 retries: 5
20
21 # n8n Application
22 n8n:
23 image: n8nio/n8n
24 container_name: n8n
25 restart: unless-stopped
26 depends_on:
27 postgres:
28 condition: service_healthy
29 ports:
30 - "5678:5678"
31 environment:
32 # Database
33 - DB_TYPE=postgresdb
34 - DB_POSTGRESDB_HOST=postgres
35 - DB_POSTGRESDB_PORT=5432
36 - DB_POSTGRESDB_DATABASE=n8n
37 - DB_POSTGRESDB_USER=n8n
38 - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
39
40 # n8n Settings
41 - N8N_BASIC_AUTH_ACTIVE=true
42 - N8N_BASIC_AUTH_USER=${N8N_USER}
43 - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
44 - N8N_HOST=${N8N_HOST}
45 - N8N_PORT=5678
46 - N8N_PROTOCOL=https
47 - WEBHOOK_URL=https://${N8N_HOST}/
48
49 # Timezone
50 - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
51 - TZ=Asia/Ho_Chi_Minh
52
53 # Execution Settings
54 - EXECUTIONS_DATA_PRUNE=true
55 - EXECUTIONS_DATA_MAX_AGE=168
56
57 volumes:
58 - n8n_data:/home/node/.n8n
59
60volumes:
61 postgres_data:
62 n8n_data:

Environment File (.env):

Bash
1# .env file (same directory as docker-compose.yml)
2
3# PostgreSQL
4POSTGRES_PASSWORD=super_secure_password_123
5
6# n8n
7N8N_HOST=n8n.yourdomain.com
8N8N_USER=admin
9N8N_PASSWORD=another_secure_password_456
Security
  • Never commit .env to git
  • Add .env to .gitignore
  • Use strong passwords (20+ chars)

Adding Nginx Reverse Proxy

With SSL (Let's Encrypt):

yaml
1version: "3.8"
2
3services:
4 # Nginx Reverse Proxy
5 nginx:
6 image: nginx:alpine
7 container_name: n8n-nginx
8 restart: unless-stopped
9 ports:
10 - "80:80"
11 - "443:443"
12 volumes:
13 - ./nginx.conf:/etc/nginx/nginx.conf:ro
14 - ./ssl:/etc/nginx/ssl:ro
15 - ./certbot:/var/www/certbot:ro
16 depends_on:
17 - n8n
18
19 # Let's Encrypt Certbot
20 certbot:
21 image: certbot/certbot
22 container_name: n8n-certbot
23 volumes:
24 - ./ssl:/etc/letsencrypt
25 - ./certbot:/var/www/certbot
26 entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
27
28 postgres:
29 # ... same as before
30
31 n8n:
32 # ... same as before
33 # Remove ports section (nginx handles external traffic)

nginx.conf:

nginx
1events {
2 worker_connections 1024;
3}
4
5http {
6 # Redirect HTTP to HTTPS
7 server {
8 listen 80;
9 server_name n8n.yourdomain.com;
10
11 location /.well-known/acme-challenge/ {
12 root /var/www/certbot;
13 }
14
15 location / {
16 return 301 https://$host$request_uri;
17 }
18 }
19
20 # HTTPS Server
21 server {
22 listen 443 ssl http2;
23 server_name n8n.yourdomain.com;
24
25 ssl_certificate /etc/nginx/ssl/live/n8n.yourdomain.com/fullchain.pem;
26 ssl_certificate_key /etc/nginx/ssl/live/n8n.yourdomain.com/privkey.pem;
27
28 # SSL Settings
29 ssl_protocols TLSv1.2 TLSv1.3;
30 ssl_prefer_server_ciphers on;
31 ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
32
33 # Proxy Settings
34 location / {
35 proxy_pass http://n8n:5678;
36 proxy_http_version 1.1;
37 proxy_set_header Upgrade $http_upgrade;
38 proxy_set_header Connection "upgrade";
39 proxy_set_header Host $host;
40 proxy_set_header X-Real-IP $remote_addr;
41 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
42 proxy_set_header X-Forwarded-Proto $scheme;
43
44 # Timeouts for long-running workflows
45 proxy_connect_timeout 300;
46 proxy_send_timeout 300;
47 proxy_read_timeout 300;
48 }
49 }
50}

Initial SSL Setup:

Bash
1# First time: get certificate
2docker compose run --rm certbot certonly \
3 --webroot \
4 --webroot-path=/var/www/certbot \
5 --email your@email.com \
6 --agree-tos \
7 --no-eff-email \
8 -d n8n.yourdomain.com

Using Traefik (Alternative)

Traefik vs Nginx:

Text
1NGINX TRAEFIK
2───── ───────
3Manual config Auto-discovery
4Manual SSL Auto Let's Encrypt
5Static Dynamic
6Simpler More features
7
8Recommendation: Nginx for single app
9 Traefik for multiple apps

Traefik Setup:

yaml
1version: "3.8"
2
3services:
4 traefik:
5 image: traefik:v2.10
6 container_name: traefik
7 restart: unless-stopped
8 command:
9 - "--api.dashboard=true"
10 - "--providers.docker=true"
11 - "--providers.docker.exposedbydefault=false"
12 - "--entrypoints.web.address=:80"
13 - "--entrypoints.websecure.address=:443"
14 - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
15 - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
16 - "--certificatesresolvers.letsencrypt.acme.email=your@email.com"
17 - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
18 ports:
19 - "80:80"
20 - "443:443"
21 volumes:
22 - /var/run/docker.sock:/var/run/docker.sock:ro
23 - ./letsencrypt:/letsencrypt
24
25 n8n:
26 image: n8nio/n8n
27 container_name: n8n
28 restart: unless-stopped
29 labels:
30 - "traefik.enable=true"
31 - "traefik.http.routers.n8n.rule=Host(`n8n.yourdomain.com`)"
32 - "traefik.http.routers.n8n.entrypoints=websecure"
33 - "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
34 - "traefik.http.services.n8n.loadbalancer.server.port=5678"
35 environment:
36 # ... same as before
37 volumes:
38 - n8n_data:/home/node/.n8n

Adding Redis (Queue Mode)

For High Availability:

yaml
1services:
2 redis:
3 image: redis:7-alpine
4 container_name: n8n-redis
5 restart: unless-stopped
6 volumes:
7 - redis_data:/data
8 healthcheck:
9 test: ["CMD", "redis-cli", "ping"]
10 interval: 10s
11 timeout: 5s
12 retries: 5
13
14 n8n:
15 # ... existing config
16 environment:
17 # Add queue mode settings
18 - EXECUTIONS_MODE=queue
19 - QUEUE_BULL_REDIS_HOST=redis
20 - QUEUE_BULL_REDIS_PORT=6379
21 - QUEUE_HEALTH_CHECK_ACTIVE=true
22 depends_on:
23 redis:
24 condition: service_healthy
25 postgres:
26 condition: service_healthy
27
28volumes:
29 redis_data:

Complete Production Stack

Full docker-compose.yml:

yaml
1version: "3.8"
2
3services:
4 nginx:
5 image: nginx:alpine
6 container_name: n8n-nginx
7 restart: unless-stopped
8 ports:
9 - "80:80"
10 - "443:443"
11 volumes:
12 - ./nginx.conf:/etc/nginx/nginx.conf:ro
13 - ./ssl:/etc/nginx/ssl:ro
14 - ./certbot:/var/www/certbot:ro
15 depends_on:
16 - n8n
17 networks:
18 - n8n-network
19
20 postgres:
21 image: postgres:15-alpine
22 container_name: n8n-postgres
23 restart: unless-stopped
24 environment:
25 POSTGRES_USER: n8n
26 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
27 POSTGRES_DB: n8n
28 volumes:
29 - postgres_data:/var/lib/postgresql/data
30 healthcheck:
31 test: ["CMD-SHELL", "pg_isready -U n8n"]
32 interval: 10s
33 timeout: 5s
34 retries: 5
35 networks:
36 - n8n-network
37
38 redis:
39 image: redis:7-alpine
40 container_name: n8n-redis
41 restart: unless-stopped
42 command: redis-server --appendonly yes
43 volumes:
44 - redis_data:/data
45 healthcheck:
46 test: ["CMD", "redis-cli", "ping"]
47 interval: 10s
48 timeout: 5s
49 retries: 5
50 networks:
51 - n8n-network
52
53 n8n:
54 image: n8nio/n8n
55 container_name: n8n
56 restart: unless-stopped
57 depends_on:
58 postgres:
59 condition: service_healthy
60 redis:
61 condition: service_healthy
62 environment:
63 # Database
64 - DB_TYPE=postgresdb
65 - DB_POSTGRESDB_HOST=postgres
66 - DB_POSTGRESDB_PORT=5432
67 - DB_POSTGRESDB_DATABASE=n8n
68 - DB_POSTGRESDB_USER=n8n
69 - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
70
71 # Queue Mode
72 - EXECUTIONS_MODE=queue
73 - QUEUE_BULL_REDIS_HOST=redis
74 - QUEUE_BULL_REDIS_PORT=6379
75
76 # n8n Config
77 - N8N_BASIC_AUTH_ACTIVE=true
78 - N8N_BASIC_AUTH_USER=${N8N_USER}
79 - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
80 - N8N_HOST=${N8N_HOST}
81 - N8N_PORT=5678
82 - N8N_PROTOCOL=https
83 - WEBHOOK_URL=https://${N8N_HOST}/
84
85 # Timezone
86 - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
87 - TZ=Asia/Ho_Chi_Minh
88
89 # Execution
90 - EXECUTIONS_DATA_PRUNE=true
91 - EXECUTIONS_DATA_MAX_AGE=168
92
93 volumes:
94 - n8n_data:/home/node/.n8n
95 networks:
96 - n8n-network
97
98volumes:
99 postgres_data:
100 redis_data:
101 n8n_data:
102
103networks:
104 n8n-network:
105 driver: bridge

Useful Commands

Bash
1# Start stack
2docker compose up -d
3
4# View all logs
5docker compose logs -f
6
7# View specific service logs
8docker compose logs -f n8n
9
10# Restart single service
11docker compose restart n8n
12
13# Scale service (queue mode)
14docker compose up -d --scale n8n=3
15
16# Update images
17docker compose pull
18docker compose up -d
19
20# Full cleanup
21docker compose down -v --rmi all
22
23# Check status
24docker compose ps

Bài Tập Thực Hành

Hands-On Challenge

Build your production stack:

  1. Create directory structure:

    Text
    1n8n/
    2├── docker-compose.yml
    3├── .env
    4├── nginx.conf
    5└── README.md
  2. Start with basic stack (n8n + postgres)

  3. Add nginx reverse proxy

  4. Configure SSL (self-signed for testing)

  5. Test webhook URL externally

  6. Document your setup in README

Share your docker-compose in community! 📦

Tiếp Theo

Bài tiếp theo: Cloud Platforms - Deploy n8n trên Railway, Render, và DigitalOcean một cách nhanh chóng.