1. Why should you run n8n in Docker for production?
The official n8n Docker image (n8nio/n8n) bundles Node.js, the n8n application, and all core dependencies. Running n8n in a container prevents version conflicts with other software on the host. Container recreation for updates takes seconds, and you can scale workers horizontally by launching additional containers.
– Stateless containers + persistent volumes = safe upgrades
– Environment variables configure every aspect without rebuilding
– One command (
docker compose up -d) starts the full stack
2. What is the minimal production Docker Compose file for n8n?
/home/node/.n8n, set N8N_HOST and WEBHOOK_URL, pin a specific image version (not :latest), and expose port 5678 only behind a reverse proxy.
version: '3.8'
services:
n8n:
image: n8nio/n8n:1.78.1
restart: unless-stopped
environment:
- N8N_HOST=n8n.yourdomain.com
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.yourdomain.com
- N8N_ENCRYPTION_KEY=your-strong-64-char-key
- N8N_USER_FOLDER=/home/node/.n8n
volumes:
- n8n_data:/home/node/.n8n
ports:
- "127.0.0.1:5678:5678"
volumes:
n8n_data:
Bind the volume to /home/node/.n8n to store SQLite (if used), credentials, and configuration. Bind port 5678 only to localhost (127.0.0.1) when a reverse proxy runs on the same host. For multi‑node setups, switch to PostgreSQL and use a shared network.
3. Which environment variables are essential for n8n Docker deployments?
N8N_HOST (public domain), N8N_PROTOCOL=https, WEBHOOK_URL, N8N_ENCRYPTION_KEY, and optionally DB_TYPE/DB_POSTGRESDB_* for PostgreSQL. Without N8N_HOST, webhook URLs become invalid.
n8n reads over 60 environment variables. The most critical ones are:
| Variable | Purpose | Example |
|---|---|---|
N8N_HOST |
Public domain for n8n | n8n.example.com |
N8N_PROTOCOL |
Set to https when behind TLS proxy |
https |
WEBHOOK_URL |
Base URL for webhook calls (overrides N8N_HOST) |
https://n8n.example.com |
N8N_ENCRYPTION_KEY |
Master key for credential AES‑256‑CBC | random 64‑char string |
EXECUTIONS_TIMEOUT |
Max workflow runtime in seconds | 3600 (default) |
If you use PostgreSQL, also set DB_TYPE=postgresdb, DB_POSTGRESDB_HOST, DB_POSTGRESDB_PORT, DB_POSTGRESDB_DATABASE, DB_POSTGRESDB_USER, and DB_POSTGRESDB_PASSWORD. The complete reference is available in the official environment variables docs.
4. How do you persist n8n data across container restarts?
/home/node/.n8n. This folder contains the SQLite database (if used), credential encryption config, and workflow templates. Without volume mapping, all data disappears when the container stops.
Docker containers are ephemeral by design. The n8n image writes all persistent data to /home/node/.n8n. By mapping a named volume or a bind mount to that path, you decouple data from the container lifecycle.
volumes:
- n8n_data:/home/node/.n8n # named volume
# OR
- ./n8n-data:/home/node/.n8n # bind mount (for easy backup)
/home/node/.n8n together with your N8N_ENCRYPTION_KEY. Without the encryption key, even a full volume backup cannot restore credentials.
For PostgreSQL users, backup the database separately using pg_dump. Volume snapshots are also valid but ensure the database is in a consistent state (stop n8n before backing up).
5. How do you connect n8n Docker to an external PostgreSQL database?
DB_TYPE=postgresdb and provide PostgreSQL connection variables (DB_POSTGRESDB_HOST, DB_POSTGRESDB_DATABASE, etc.). n8n automatically migrates the schema on first startup. Use a separate PostgreSQL container or a managed cloud database.
Replace the default SQLite engine with PostgreSQL to enable queue mode and support higher concurrency. A typical Docker Compose stack includes both n8n and PostgreSQL services:
services:
n8n:
image: n8nio/n8n:1.78.1
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=securepass
postgres:
image: postgres:15
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=securepass
- POSTGRES_DB=n8n
volumes:
- postgres_data:/var/lib/postgresql/data
n8n automatically creates the required tables. Do not manually alter the schema. For migration from SQLite to PostgreSQL, use n8n export:workflow --all and n8n import:workflow after reconfiguring the database connection.
6. How do you configure a reverse proxy (Nginx/Caddy) for an n8n Docker container?
N8N_PROTOCOL=https inside the n8n container to generate correct webhook URLs.
n8n does not terminate HTTPS. A reverse proxy adds SSL/TLS, improves security, and offloads certificate management. For Nginx, the critical part is upgrading the WebSocket connection:
location / {
proxy_pass http://n8n:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
When using Caddy, automatic HTTPS is built‑in. A simple Caddyfile entry: n8n.yourdomain.com { reverse_proxy n8n:5678 }. Caddy automatically handles WebSocket upgrades. Always set N8N_PROTOCOL=https so n8n knows to generate secure webhook URLs. Full reverse proxy examples are in the official reverse proxy documentation.
7. How do you safely upgrade n8n Docker without downtime?
docker compose pull && docker compose up -d. Data stays in persistent volumes. Test the new version in staging first and always pin specific tags (e.g., :1.78.1), never :latest.
Never use :latest in production because it changes unpredictably. Instead, refer to the official Docker Hub tags and select a specific version like 1.78.1. Upgrade procedure:
docker compose stop n8n
docker compose pull n8n
docker compose up -d
Because the persistent volume remains unchanged, no data loss occurs. For major version upgrades (e.g., from 0.x to 1.x), read the release notes for potential breaking changes. Rollback by reverting the image tag and restarting.
8. What are common n8n Docker startup errors and how do you fix them?
N8N_HOST leading to invalid webhook URLs, database connection failures, or permission issues on bind mounts. Check container logs with docker logs n8n.
Run docker logs <container_name> to see real‑time errors. Frequent issues:
- “ECONNREFUSED” for PostgreSQL → database container not ready; add
depends_onwith healthcheck. - “Invalid webhook URL” →
N8N_HOSTorWEBHOOK_URLnot matching the public domain. - “Credentials cannot be decrypted” →
N8N_ENCRYPTION_KEYchanged after data was written. Restore original key or restore backup. - Permission denied on bind mount → ensure the host directory is owned by UID 1000 (the
nodeuser inside the container).
docker compose up --scale n8n=2 to test queue mode locally.
📖 Official references (rel=”nofollow”)

