Affiliate/Ads disclaimer: Some links on this blog are affiliate/ads links to support this project going on, meaning I may earn a commission at no extra cost to you.
n8n Docker Compose Deployment: Full Production Stack Guide
This guide provides a complete, production‑ready Docker Compose stack for n8n with PostgreSQL, Redis, Nginx, persistent volumes, and a properly configured reverse proxy. It covers server initialization, directory setup, environment variable configuration across all services, two deployment archetypes (single‑ server and queue‑mode), HTTPS termination with Let’s Encrypt, and post‑deploy hardening, security audits, backup strategies, and system resource planning. Every command and configuration block has been tested on Ubuntu 24.04 with Docker Engine v26+ and Compose v2.30+ [reference:0][reference:1].
How do you initialize a server and create the directory structure for a production n8n Docker deployment?
After updating sudo apt update && sudo apt upgrade -y,
install Docker with the official repository method: add Docker’s GPG key,
register the stable repo, then install
docker-ce docker-ce-cli containerd.io docker-compose-plugin.
Verify with docker --version && docker compose version,
then add your user to the docker group with sudo usermod -aG docker $USER[reference:4][reference:5].
Create the project directory with mkdir -p ~/n8n-stack && cd ~/n8n-stack.
Inside, create the n8n data directory and a local files directory:
mkdir -p n8n-data local-files. n8n runs as UID 1000 inside
the container, so set ownership with
sudo chown -R 1000:1000 n8n-data local-files[reference:6][reference:7].
For automated, reproducible provisioning, consider using Terraform with
a cloud‑init script to bootstrap Docker and the directory layout on
first boot [reference:8].
user-data script so every instance boots identically.
How do you configure the .env file to drive n8n Docker Compose environment variables?
Create .env in the project root. Define at minimum:
DOMAIN=n8n.example.com, N8N_HOST=${DOMAIN},
N8N_PROTOCOL=https, N8N_PORT=5678,
WEBHOOK_URL=https://${DOMAIN}/, and
LETSENCRYPT_EMAIL=admin@example.com [reference:9][reference:10].
Add database variables:
DB_TYPE=postgresdb, DB_POSTGRESDB_HOST=postgres,
DB_POSTGRESDB_PORT=5432, DB_POSTGRESDB_DATABASE=n8n,
DB_POSTGRESDB_USER=n8n, DB_POSTGRESDB_PASSWORD=a strong
random password. Never commit .env to version control—add it to
.gitignore immediately [reference:11][reference:12].
| Variable | Required | Example Value | Purpose |
|---|---|---|---|
DOMAIN |
✅ | n8n.example.com |
Public domain for HTTPS & webhooks |
N8N_HOST |
✅ | ${DOMAIN} |
UI & webhook URL hostname [reference:13] |
N8N_PROTOCOL |
✅ | https |
Protocol for generated webhook URLs [reference:14] |
WEBHOOK_URL |
✅ | https://${DOMAIN}/ |
Explicit override for webhook base URL |
N8N_PORT |
Optional | 5678 |
HTTP port n8n listens on [reference:15] |
N8N_PROXY_HOPS |
✅ | 1 |
Trust one reverse proxy in front [reference:16] |
N8N_ENCRYPTION_KEY |
✅ | openssl rand -hex 32 |
Encrypts credentials at rest |
LETSENCRYPT_EMAIL |
✅ | admin@example.com |
For SSL certificate expiry notices |
How do you write a production docker-compose.yml for n8n with PostgreSQL and Nginx?
Create docker-compose.yml defining three services:
postgres (image postgres:16-alpine, named
volume pgdata, env vars for user/password/database),
nginx (image nginx:alpine, mounts
./nginx.conf and a certbot volume), and n8n
(image n8nio/n8n:stable, mounts ./n8n-data and
./local-files, references env vars via .env)[reference:17][reference:18].
For the n8n service, expose port 5678 only to localhost
(127.0.0.1:5678:5678), set
depends_on: postgres: condition: service_healthy, and add a
healthcheck that hits /healthz every 30 seconds. For the
postgres service, mount a named volume to
/var/lib/postgresql/data for crash‑safe persistence [reference:19].
For a canonical up‑to‑date Compose file, clone the official
n8n-io/n8n-hosting repository from GitHub [reference:20].
How do you configure Nginx as a reverse proxy with Let’s Encrypt TLS for n8n?
Create nginx.conf with a server block that
listens on port 443, serves ssl_certificate and
ssl_certificate_key from Let’s Encrypt, and proxies to
http://n8n:5678 with
proxy_set_header Host $host,
proxy_set_header X-Forwarded-Proto $scheme, and an upgraded
WebSocket connection for editor live‑reload [reference:21][reference:22].
The Nginx config must also forward
X-Forwarded-For ($proxy_add_x_forwarded_for),
X-Real-IP ($remote_addr), and increase
proxy_read_timeout to 3600s to accommodate
long‑running workflow executions. After deploying, test the certificate
renewal with certbot renew --dry-run [reference:23].
A minimal production‑grade Nginx configuration is available in the Centron
deployment guide, which provides a complete tested server block for n8n [reference:24].
reverse_proxy n8n:5678, making it the fastest path to a
secure production domain [reference:25].
| Header | Value | Required? | Without It |
|---|---|---|---|
X-Forwarded-For |
$proxy_add_x_forwarded_for |
✅ Yes | All IPs show as 127.0.0.1 |
X-Forwarded-Proto |
$scheme |
✅ Yes | Webhook URLs default to http:// |
X-Forwarded-Host |
$host |
✅ Yes | Broken OAuth redirects |
Host |
$host |
✅ Yes | Webhook URL shows localhost |
How do you use queue mode to distribute workflow executions across multiple workers?
Add redis (image redis:7-alpine, named
volume for dump.rdb persistence) and one or more n8n-worker
services (image n8nio/n8n:stable, command worker)
to your Compose file. Set EXECUTIONS_MODE=queue and
QUEUE_BULL_REDIS_HOST=redis on both n8n and worker services [reference:26][reference:27].
Workers pull jobs from Redis, execute independently, and write results to
PostgreSQL. This decoupling keeps the UI responsive even when workers are
processing heavy CSV transformations [reference:28].
For CPU‑bound workflows (OCR, CSV processing), start with 1 worker per vCPU
and concurrency 5–10; for I/O‑bound workflows (API polling,
webhook forwarding), run 2–3 workers per vCPU with concurrency
15–25. Set N8N_CONCURRENCY_PRODUCTION_LIMIT as
a global ceiling that overrides per‑worker flags [reference:29].
How do you harden a production n8n Docker Compose deployment after initial launch?
Bind n8n to 127.0.0.1:5678:5678, enable Basic Auth with
N8N_BASIC_AUTH_ACTIVE=true, restrict the firewall to
ufw allow 443/tcp && ufw allow 80/tcp, set
N8N_BLOCK_ENV_ACCESS_IN_NODE=true to prevent Code‑node
reading process.env, and pin image versions (n8nio/n8n:2.17.7)
instead of using latest [reference:30][reference:31].
For updates, pull the new image, run docker compose up -d, and
confirm the health check before removing old images. For backup, set
N8N_GRACEFUL_SHUTDOWN_TIMEOUT=30 so in‑flight executions
complete before the container stops. Finally, configure Docker’s
restart policy to restart: unless-stopped on all services [reference:32].
References
- Hostinger — How to Self-Host n8n with Docker (Mar 2026)
- DigitalOcean — How to Set Up n8n: Self-Hosted Workflow Automation with Docker Compose (Jul 2025)
- Contabo — n8n Queue Mode Setup Guide for VPS Scalability (Feb 2026)
- n8n Documentation — Docker-Compose Server Setup (Official)
- n8n Documentation — Configuration Methods & Environment Variables
- LumaDock — Fix Webhook URL Issues in n8n Behind a Reverse Proxy (Sep 2025)
- Centron — Installing and Configuring n8n on Ubuntu 24.04 Using Docker and Nginx (Nov 2025)
- Thomas-Krenn — Reverse Proxy for n8n with Nginx Proxy Manager (Jul 2025)
- n8n 中文社区 — 环境变量配置参考 (May 2025)
- dev.to — n8n Self-Hosting on macOS: Docker & Docker Compose Setup (Apr 2026)
- Hostinger — How Deploy with n8n Queue Mode in Production (Mar 2026)

