n8n Docker Compose Deployment: Full Production Stack Guide

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.


Published: May 4, 2026
Updated: May 7, 2026
n8n Docker Compose Deployment: Full Production Stack Guide
⚡ n8n Workflow Automation T4 · Docker Production Stack
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].

📋 Prerequisites: Ubuntu 24.04 (or 22.04) VPS, Docker Engine ≥ v20.10.0 with Compose plugin, a domain with an A/AAAA DNS record pointing to the server, and ports 80 + 443 open in the firewall. For production workloads, baseline hardware is 2 vCPU / 4 GB RAM / 40 GB SSD; for queue‑mode scale above ~200 executions per day, upgrade to 4 vCPU / 8 GB+ RAM [reference:2][reference:3].

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].

💡 Reproducible Provisioning: For staging and production parity, script the entire initialization—Docker installation, user setup, directory creation, and firewall rules—using a cloud‑init 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].

⚡ HTTPS Quick‑Start: If maintaining Nginx and Let’s Encrypt feels like overhead, consider using Caddy or Traefik as your reverse proxy. Caddy auto‑negotiates TLS with zero configuration beyond 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

This guide is for informational purposes only. Docker image tags, package versions, and environment variable defaults may change. Always refer to the official n8n hosting documentation, Docker documentation, and Nginx documentation for the most current configuration reference.

Leave a Reply

Your email address will not be published. Required fields are marked *