Self-hosting n8n with Docker Compose
Updated 2026-10-21 · 5 min read
Self-hosting n8n with Docker gives you everything n8n Cloud restricts: npm packages in Code nodes, Python with libraries, no per-execution pricing, and data on your own server. In exchange you run the database, the runners, backups and upgrades. This guide goes from the one-line test container to a production Compose file with Postgres and the task runner sidecar that n8n 2.x needs for Code nodes and Python.
n8n itself says self-hosting is recommended for expert users (Docker docs). If you've never operated a database in production, read n8n Cloud vs self-hosted first.
Step 0: try it in one command
docker volume create n8n_data
docker run -it --rm --name n8n -p 5678:5678 \
-e GENERIC_TIMEZONE="Europe/Berlin" -e TZ="Europe/Berlin" \
-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
-v n8n_data:/home/node/.n8n \
n8nio/n8n
Open http://localhost:5678 and create the owner account. Data, including the SQLite database and the encryption key, lives in the n8n_data volume. Good for evaluation, not for production: no Postgres, no external runners, no TLS.
What a production setup needs
| Piece | Why |
|---|---|
| Postgres | n8n recommends it over SQLite for real workloads |
n8nio/runners sidecar |
External-mode task runners: real isolation for Code nodes, and required for Python |
Fixed N8N_ENCRYPTION_KEY |
Credentials stay readable across container recreation |
| Timezone | Self-hosted default is America/New_York |
| Reverse proxy with TLS | HTTPS, and correct public webhook URLs |
| Backups | Database dump plus the key |
The Compose file
Create a folder with .env and compose.yml.
.env (keep it out of version control):
N8N_VERSION=2.44.4
POSTGRES_USER=n8n
POSTGRES_PASSWORD=change-me
POSTGRES_DB=n8n
N8N_ENCRYPTION_KEY=change-me-long-random-string
N8N_RUNNERS_AUTH_TOKEN=change-me-another-random-string
Pin N8N_VERSION to a current stable release; the runners image must use the same version as n8n (task runners).
compose.yml:
services:
postgres:
image: postgres:18
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
PGDATA: /var/lib/postgresql/data
volumes:
- db-storage:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
n8n:
image: n8nio/n8n:${N8N_VERSION}
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: "5432"
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
GENERIC_TIMEZONE: Europe/Berlin
TZ: Europe/Berlin
N8N_HOST: n8n.example.com
N8N_PROTOCOL: https
N8N_WEBHOOK_URL: https://n8n.example.com/
N8N_PROXY_HOPS: "1"
N8N_RUNNERS_MODE: external
N8N_RUNNERS_BROKER_LISTEN_ADDRESS: 0.0.0.0
N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
volumes:
- n8n-data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
task-runners:
image: n8nio/runners:${N8N_VERSION}
restart: always
environment:
N8N_RUNNERS_TASK_BROKER_URI: http://n8n:5679
N8N_RUNNERS_AUTH_TOKEN: ${N8N_RUNNERS_AUTH_TOKEN}
depends_on:
- n8n
volumes:
db-storage:
n8n-data:
Start it:
docker compose up -d
docker compose logs -f n8n
Notes on the choices:
- Postgres 18 with
PGDATAset: n8n's Compose guide uses this so the data path matches earlier Postgres versions. - Port bound to 127.0.0.1: only the reverse proxy on the same host can reach n8n directly.
N8N_WEBHOOK_URL(n8n 2.30.0+; older versions useWEBHOOK_URL): without it, webhook URLs in the editor showlocalhost:5678and senders can't reach them.N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0: the broker listens only on localhost by default; the sidecar needs to connect from another container.N8N_RUNNERS_ENABLEDisn't needed: it's deprecated from 2.0, where task runners are on by default.
The n8n Compose guide also bundles a sandbox stack for the n8n Assistant (AI code execution). It's optional and needs more resources (at least 4 GB RAM and 2 vCPUs per that guide); the file above leaves it out.
Reverse proxy
Put Caddy, Traefik or nginx in front for TLS. With Caddy the whole config is:
n8n.example.com {
reverse_proxy 127.0.0.1:5678
}
Keep N8N_PROXY_HOPS equal to the number of proxies in front of n8n, or IP allowlists on webhooks see the proxy's address.
npm packages and Python libraries
Packages for Code nodes are installed in the runners image and allow-listed in its launcher config, not in the main n8n container:
FROM n8nio/runners:2.44.4
USER root
RUN cd /opt/runners/task-runner-javascript && pnpm add lodash dayjs
RUN cd /opt/runners/task-runner-python && uv pip install pandas
COPY n8n-task-runners.json /etc/n8n-task-runners.json
USER runner
In n8n-task-runners.json, set NODE_FUNCTION_ALLOW_EXTERNAL (JavaScript) and N8N_RUNNERS_EXTERNAL_ALLOW (Python) under each runner's env-overrides. Build the image and use it instead of n8nio/runners in the Compose file. Step-by-step with the full config: npm packages in n8n and Python Code node.
Backups
- Database:
docker compose exec postgres pg_dump -U n8n n8n > n8n-$(date +%F).sql, daily, copied off the server. - Encryption key: stored in your
.env, and in then8n-datavolume. Without it, restored credentials are unreadable. - Workflows as files (optional):
n8n export:workflow --backup --output=backups/inside the container gives you JSON for Git.
Test a restore once on a second machine before you rely on it.
Upgrades
- Read the release notes for breaking changes (2.0 changed Code node runners, Python, env access and publishing).
- Back up the database.
- Change
N8N_VERSIONin.env; both stock images follow it. If you built a custom runners image, rebuild it from the same version first. docker compose pull && docker compose up -d. n8n runs database migrations on start.
Free vs licensed features
The self-hosted Community edition is free and includes almost all of n8n. It lacks, among others, Variables, environments, external secrets, log streaming, SSO, credential sharing, projects and Git source control; registering your email adds folders, debug in editor and custom execution data (Community edition features). Business and Enterprise licenses unlock the rest.
When to scale
One n8n container handles a lot. When executions queue up or the editor gets slow during heavy runs, move to queue mode: Redis plus worker containers, each with its own runners sidecar.
Self-hosting as a Pipedream replacement
Teams leaving Pipedream often self-host because Pipedream code steps imported any npm package, and n8n Cloud allows only crypto and moment in the Code node. Before you choose, count how many of your steps actually import packages: our converter report lists every import per workflow, and the code step checker does it for one step. The report and 1 workflow are free. Full export: $3 per workflow, $19 minimum — coming soon. Then follow migrate Pipedream to n8n with the instance you just set up.
Not affiliated with Pipedream or n8n.
FAQ
- What is the quickest way to run n8n in Docker?
- docker volume create n8n_data, then docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n n8nio/n8n. Open http://localhost:5678. Use Docker Compose with Postgres for anything long-lived.
- Do I need the n8nio/runners image?
- For Python Code nodes and for isolating JavaScript Code nodes properly, yes. External-mode task runners run in an n8nio/runners sidecar whose version must match n8nio/n8n; since 2.0 the main image doesn't include the external-mode runner.
- Should n8n use SQLite or Postgres?
- SQLite is the default and fine for testing. n8n's Compose guide recommends Postgres for production instances with more than a handful of users or workflows running around the clock.
- How much RAM does self-hosted n8n need?
- n8n's own Docker page doesn't state minimum hardware for n8n alone. Its full Compose guide, which includes the n8n Assistant sandbox stack, asks for at least 4 GB RAM and 2 vCPUs.
- What must I back up?
- The database (or the n8n_data volume if you use SQLite) and the encryption key. Credentials can't be decrypted without the key.
Related
Sources
- https://docs.n8n.io/deploy/host-n8n/install-options/install-with-docker
- https://docs.n8n.io/deploy/host-n8n/install-options/install-using-docker-compose
- https://docs.n8n.io/deploy/host-n8n/configure-n8n/set-up-task-runners
- https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables/deployment
- https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables/endpoints
- https://docs.n8n.io/deploy/host-n8n/community-edition-features