📦 Docker Compose cho n8n
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.yml4 --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 start13 ✅ Automatic networking14 ✅ Service dependenciesBasic Structure:
yaml
1# docker-compose.yml2 3version: "3.8"4 5services:6 service1:7 image: image_name8 ports:9 - "host:container"10 environment:11 - VAR=value12 volumes:13 - volume:/path14 depends_on:15 - service216 17 service2:18 image: another_image19 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/n8n6 container_name: n8n7 restart: unless-stopped8 ports:9 - "5678:5678"10 environment:11 - N8N_BASIC_AUTH_ACTIVE=true12 - N8N_BASIC_AUTH_USER=admin13 - N8N_BASIC_AUTH_PASSWORD=changeme14 - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh15 volumes:16 - n8n_data:/home/node/.n8n17 18volumes:19 n8n_data:Commands:
Bash
1# Start all services2docker compose up -d3 4# View logs5docker compose logs -f6 7# Stop8docker compose down9 10# Stop and remove volumes (careful!)11docker compose down -v12 13# Rebuild after changes14docker compose up -d --buildProduction Setup với PostgreSQL
Why PostgreSQL?
Text
1SQLITE (Default) POSTGRESQL2──────────────── ──────────3✅ Zero config ✅ Better performance4✅ Good for dev ✅ Concurrent access5❌ Single file ✅ Better backups6❌ Not scalable ✅ Replication ready7❌ Locking issues ✅ Production standardFull Stack docker-compose.yml:
yaml
1version: "3.8"2 3services:4 # PostgreSQL Database5 postgres:6 image: postgres:15-alpine7 container_name: n8n-postgres8 restart: unless-stopped9 environment:10 POSTGRES_USER: n8n11 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}12 POSTGRES_DB: n8n13 volumes:14 - postgres_data:/var/lib/postgresql/data15 healthcheck:16 test: ["CMD-SHELL", "pg_isready -U n8n"]17 interval: 10s18 timeout: 5s19 retries: 520 21 # n8n Application22 n8n:23 image: n8nio/n8n24 container_name: n8n25 restart: unless-stopped26 depends_on:27 postgres:28 condition: service_healthy29 ports:30 - "5678:5678"31 environment:32 # Database33 - DB_TYPE=postgresdb34 - DB_POSTGRESDB_HOST=postgres35 - DB_POSTGRESDB_PORT=543236 - DB_POSTGRESDB_DATABASE=n8n37 - DB_POSTGRESDB_USER=n8n38 - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}39 40 # n8n Settings41 - N8N_BASIC_AUTH_ACTIVE=true42 - N8N_BASIC_AUTH_USER=${N8N_USER}43 - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}44 - N8N_HOST=${N8N_HOST}45 - N8N_PORT=567846 - N8N_PROTOCOL=https47 - WEBHOOK_URL=https://${N8N_HOST}/48 49 # Timezone50 - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh51 - TZ=Asia/Ho_Chi_Minh52 53 # Execution Settings54 - EXECUTIONS_DATA_PRUNE=true55 - EXECUTIONS_DATA_MAX_AGE=16856 57 volumes:58 - n8n_data:/home/node/.n8n59 60volumes:61 postgres_data:62 n8n_data:Environment File (.env):
Bash
1# .env file (same directory as docker-compose.yml)2 3# PostgreSQL4POSTGRES_PASSWORD=super_secure_password_1235 6# n8n7N8N_HOST=n8n.yourdomain.com8N8N_USER=admin9N8N_PASSWORD=another_secure_password_456Security
- Never commit .env to git
- Add
.envto.gitignore - Use strong passwords (20+ chars)
Adding Nginx Reverse Proxy
With SSL (Let's Encrypt):
yaml
1version: "3.8"2 3services:4 # Nginx Reverse Proxy5 nginx:6 image: nginx:alpine7 container_name: n8n-nginx8 restart: unless-stopped9 ports:10 - "80:80"11 - "443:443"12 volumes:13 - ./nginx.conf:/etc/nginx/nginx.conf:ro14 - ./ssl:/etc/nginx/ssl:ro15 - ./certbot:/var/www/certbot:ro16 depends_on:17 - n8n18 19 # Let's Encrypt Certbot20 certbot:21 image: certbot/certbot22 container_name: n8n-certbot23 volumes:24 - ./ssl:/etc/letsencrypt25 - ./certbot:/var/www/certbot26 entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"27 28 postgres:29 # ... same as before30 31 n8n:32 # ... same as before33 # Remove ports section (nginx handles external traffic)nginx.conf:
nginx
1events {2 worker_connections 1024;3}4 5http {6 # Redirect HTTP to HTTPS7 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 Server21 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 Settings29 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 Settings34 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 workflows45 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 certificate2docker 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.comUsing Traefik (Alternative)
Traefik vs Nginx:
Text
1NGINX TRAEFIK2───── ───────3Manual config Auto-discovery4Manual SSL Auto Let's Encrypt5Static Dynamic6Simpler More features7 8Recommendation: Nginx for single app9 Traefik for multiple appsTraefik Setup:
yaml
1version: "3.8"2 3services:4 traefik:5 image: traefik:v2.106 container_name: traefik7 restart: unless-stopped8 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:ro23 - ./letsencrypt:/letsencrypt24 25 n8n:26 image: n8nio/n8n27 container_name: n8n28 restart: unless-stopped29 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 before37 volumes:38 - n8n_data:/home/node/.n8nAdding Redis (Queue Mode)
For High Availability:
yaml
1services:2 redis:3 image: redis:7-alpine4 container_name: n8n-redis5 restart: unless-stopped6 volumes:7 - redis_data:/data8 healthcheck:9 test: ["CMD", "redis-cli", "ping"]10 interval: 10s11 timeout: 5s12 retries: 513 14 n8n:15 # ... existing config16 environment:17 # Add queue mode settings18 - EXECUTIONS_MODE=queue19 - QUEUE_BULL_REDIS_HOST=redis20 - QUEUE_BULL_REDIS_PORT=637921 - QUEUE_HEALTH_CHECK_ACTIVE=true22 depends_on:23 redis:24 condition: service_healthy25 postgres:26 condition: service_healthy27 28volumes:29 redis_data:Complete Production Stack
Full docker-compose.yml:
yaml
1version: "3.8"2 3services:4 nginx:5 image: nginx:alpine6 container_name: n8n-nginx7 restart: unless-stopped8 ports:9 - "80:80"10 - "443:443"11 volumes:12 - ./nginx.conf:/etc/nginx/nginx.conf:ro13 - ./ssl:/etc/nginx/ssl:ro14 - ./certbot:/var/www/certbot:ro15 depends_on:16 - n8n17 networks:18 - n8n-network19 20 postgres:21 image: postgres:15-alpine22 container_name: n8n-postgres23 restart: unless-stopped24 environment:25 POSTGRES_USER: n8n26 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}27 POSTGRES_DB: n8n28 volumes:29 - postgres_data:/var/lib/postgresql/data30 healthcheck:31 test: ["CMD-SHELL", "pg_isready -U n8n"]32 interval: 10s33 timeout: 5s34 retries: 535 networks:36 - n8n-network37 38 redis:39 image: redis:7-alpine40 container_name: n8n-redis41 restart: unless-stopped42 command: redis-server --appendonly yes43 volumes:44 - redis_data:/data45 healthcheck:46 test: ["CMD", "redis-cli", "ping"]47 interval: 10s48 timeout: 5s49 retries: 550 networks:51 - n8n-network52 53 n8n:54 image: n8nio/n8n55 container_name: n8n56 restart: unless-stopped57 depends_on:58 postgres:59 condition: service_healthy60 redis:61 condition: service_healthy62 environment:63 # Database64 - DB_TYPE=postgresdb65 - DB_POSTGRESDB_HOST=postgres66 - DB_POSTGRESDB_PORT=543267 - DB_POSTGRESDB_DATABASE=n8n68 - DB_POSTGRESDB_USER=n8n69 - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}70 71 # Queue Mode72 - EXECUTIONS_MODE=queue73 - QUEUE_BULL_REDIS_HOST=redis74 - QUEUE_BULL_REDIS_PORT=637975 76 # n8n Config77 - N8N_BASIC_AUTH_ACTIVE=true78 - N8N_BASIC_AUTH_USER=${N8N_USER}79 - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}80 - N8N_HOST=${N8N_HOST}81 - N8N_PORT=567882 - N8N_PROTOCOL=https83 - WEBHOOK_URL=https://${N8N_HOST}/84 85 # Timezone86 - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh87 - TZ=Asia/Ho_Chi_Minh88 89 # Execution90 - EXECUTIONS_DATA_PRUNE=true91 - EXECUTIONS_DATA_MAX_AGE=16892 93 volumes:94 - n8n_data:/home/node/.n8n95 networks:96 - n8n-network97 98volumes:99 postgres_data:100 redis_data:101 n8n_data:102 103networks:104 n8n-network:105 driver: bridgeUseful Commands
Bash
1# Start stack2docker compose up -d3 4# View all logs5docker compose logs -f6 7# View specific service logs8docker compose logs -f n8n9 10# Restart single service11docker compose restart n8n12 13# Scale service (queue mode)14docker compose up -d --scale n8n=315 16# Update images17docker compose pull18docker compose up -d19 20# Full cleanup21docker compose down -v --rmi all22 23# Check status24docker compose psBài Tập Thực Hành
Hands-On Challenge
Build your production stack:
-
Create directory structure:
Text1n8n/2├── docker-compose.yml3├── .env4├── nginx.conf5└── README.md -
Start with basic stack (n8n + postgres)
-
Add nginx reverse proxy
-
Configure SSL (self-signed for testing)
-
Test webhook URL externally
-
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.
