AI · Automation
n8n self-hosted in production: Docker Compose, PostgreSQL and Tailscale
n8n on your own server is up in minutes, but "in production" is a different matter: PostgreSQL instead of SQLite, a pinned version, an encryption key that does not get lost, access only over a VPN and a clear update plan. Here is what n8n itself documents, and where "no downtime" is harder than it sounds.
- The original (28.04.2026) was only a title and a note.
"The full text is being prepared."→ the full text was written on 02.10.2026 from the n8n documentation. [1][2][4][5] - The description promised updating "without downtime".
"how to survive an update without downtime"→ the documented Compose procedure stops the container; real high availability exists only in multi-main mode, which for self-hosting is Enterprise. [3][8] - Added: current versions: stable 2.41.6, beta 2.42.2 as of 02.10.2026; n8n 2.0 dates from 08.12.2025 and since then tasks have run through task runners. [3][10]
- Added: supported PostgreSQL versions (17 and 18, plus 16), the
PGDATAtrap with PostgreSQL 18, the scope of the backup, the renaming ofWEBHOOK_URLtoN8N_WEBHOOK_URLand a note on the licence. [2][5][7][11] - Removed: the navigation to the shop and the "KAGAMI Hub" framing. The page is a general guide, not a description of a specific installation.
01 · THE BASISDocker Compose and a pinned version
n8n recommends Docker, and more precisely Docker Compose, for self-hosting; the prerequisites are Docker Engine and Docker Compose v2 (docker compose version). The old "Install with Docker" page is now marked by n8n as outdated in favour of the Compose page [1][3].
The version is pinned explicitly: instead of a floating tag, the number is kept in an .env file and taken from the releases page on GitHub [1][10]. That way an update is a decision, not an accident. As of 02.10.2026 the stable version is 2.41.6 and the beta is 2.42.2; n8n ships a new minor version almost every week, so the number in this article goes out of date quickly [3][10].
N8N_RUNNERS_ENABLED has been deprecated: it no longer needs to be set [3][10]. The n8n example for PostgreSQL also uses an external process to run tasks (N8N_RUNNERS_MODE=external with the separate n8nio/runners image) as a more isolated option [9].02 · THE DATABASEPostgreSQL instead of SQLite
With no other setup, n8n keeps workflows, credentials and execution history in SQLite. For a trial that is enough; for production that has to run around the clock and with more than a handful of users or workflows, n8n recommends PostgreSQL [1][2].
- Versions: as of July 2026 n8n supports the two newest actively supported versions of PostgreSQL (17 and 18) and one more for compatibility (16). The range shifts every year, so check the n8n page [2].
- The PostgreSQL 18 trap: from version 18 the data goes elsewhere by default. The line
PGDATA=/var/lib/postgresql/datamust stay, otherwise the database starts empty and the volume is not used [1]. - An old database on an earlier version: a direct jump to 18 does not work ("database files are incompatible with server"). First a backup with
pg_dumpall, then the official PostgreSQL upgrade guide [1]. - There is no automatic migration from SQLite: the n8n guide is for a new installation, not for a live migration [1].
Below is a minimal example, assembled from the documentation [1][9]. The values in <…> are placeholders: keep your own in an .env file outside version control.
N8N_VERSION=<number from the releases page on GitHub>
POSTGRES_USER=<user>
POSTGRES_PASSWORD=<long-random-password>
POSTGRES_DB=n8n
N8N_ENCRYPTION_KEY=<long-random-string>volumes:
db_storage:
n8n_storage:
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: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
restart: always
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/Sofia
TZ: Europe/Sofia
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_storage:/home/node/.n8n
depends_on:
postgres:
condition: service_healthyPGDATA, the health check and the /home/node/.n8n volume are from the n8n documentation and example [1][9]. Binding only to 127.0.0.1 is our choice; it is consistent with Tailscale's advice for services behind Serve [12]. The example is not complete: for a stricter setup (a separate database user, external task runners) see n8n's withPostgres example [1][9].03 · THE KEYThe encryption key and the .n8n volume
n8n encrypts credentials before writing them to the database. The key is created automatically on first start and is kept in the .n8n folder; if it has not been created yet, you can set your own through N8N_ENCRYPTION_KEY [6]. In Docker the folder is the volume mounted at /home/node/.n8n [5].
“a restored database or encrypted credential export can’t be decrypted”
n8n docs, Back up and restore [5] (without the encryption key, whether from the configuration file or set through N8N_ENCRYPTION_KEY)
That is why a full backup is two things together: the .n8n folder and the external database. Even with PostgreSQL the .n8n volume stays in the backup, because it holds the key [3][5]. Exporting through the command line (n8n export:workflow --backup, n8n export:credentials --backup) is useful for moving workflows, but it does not contain the users and roles, the execution history, the variables and the instance settings, including the key [5]. n8n recommends a full backup before every update [5].
04 · THE ACCESSTailscale and webhooks
The simplest way to keep the n8n editor off the internet is to leave it on an internal network. Tailscale Serve forwards traffic from the other devices in your network (tailnet) to a local service on the machine, for example tailscale serve 5678 for the n8n port, as in Tailscale's local-port example [12]. When the service relies on the identity headers that Serve adds, Tailscale advises it to listen only on localhost; otherwise someone can supply their own values directly [12].
If a reverse proxy sits in front of n8n, n8n cannot work out the external address by itself. The documentation requires: N8N_WEBHOOK_URL with the value of the public address, N8N_PROXY_HOPS=1 and the headers X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto from the last proxy [7]. N8N_WEBHOOK_URL replaces WEBHOOK_URL, which has been deprecated since n8n 2.35.0; with the old name n8n writes a warning [7].
05 · THE UPDATEAnd how "no downtime" it is
n8n's advice is simple [4]:
- Update often, at least once a month; that way you do not skip many versions at once.
- Read the release notes for breaking changes.
- Try the update first on a separate test instance.
- Make a full backup before updating [5].
The documented steps for Compose are docker compose pull, docker compose down, docker compose up -d [3]. Note what this means: the container is stopped and started again. That is a short interruption, not an update "without downtime". The page the steps come from is marked by n8n as outdated in favour of the Compose page, and that page does not describe update steps, so use them as a basis and check them in your own environment [1][3].
A pinned version makes rolling back clear: you put the previous number back in .env and restore the backup if the new version changed the database. Be careful: restoring requires the same encryption key [5].
06 · THE SCALEWhen it grows: queue mode
For heavier load n8n has queue mode: a main instance accepts the triggers and webhooks, and separate workers run the tasks through a queue in Redis. The mode is switched on with EXECUTIONS_MODE=queue; the encryption key must be the same for the main instance and all workers. Queue mode is not recommended with SQLite, one more argument for PostgreSQL [8].
Workers have a timeout for graceful shutdown (30 seconds by default) so that they can finish their current tasks [8]. That is a good habit, but it is not high availability: a second main instance needs multi-main, that is, Enterprise [8].
07 · THE LICENCEWhat the free n8n allows
n8n is under the Sustainable Use License (a "fair-code" model): you may use and modify the software for your own internal business purposes or for non-commercial and personal use; distribution is allowed only free of charge and for a non-commercial purpose. Files with ".ee." in the name are under a separate Enterprise licence [11]. If you are thinking of offering n8n as a service to others, read the licence and its frequently asked questions first. This is general information, not legal advice.
08 · THE CHECKLISTBefore it is "in production"
A pinned version
The n8n number is in
.env, taken from the releases page; PostgreSQL is 17 or 18 withPGDATAset.The key is somewhere safe
N8N_ENCRYPTION_KEYis set, and a copy of it is kept separately from the server.A full backup
The
.n8nfolder and the database together, and at least one restore checked on a test machine.Access is closed
The editor is reachable only on the internal network; it is clearly decided how (and whether) incoming webhooks reach the outside.
An update plan
A monthly rhythm, the release notes read, the test passed, a backup and a window for a short interruption.
This is general information, compiled from the n8n and Tailscale documentation, not legal or operational advice for a specific environment.
09 · SOURCESSources
- n8n docs, "Install using Docker Compose" — docs.n8n.io/…/install-using-docker-compose
- n8n docs, "Choose n8n's database" (supported PostgreSQL versions) — docs.n8n.io/…/choose-n8ns-database
- n8n docs, "Install with Docker" (stable/beta version, update steps, tunnel for webhooks; marked as outdated) — docs.n8n.io/…/install-with-docker
- n8n docs, "Update n8n" — docs.n8n.io/…/update-n8n
- n8n docs, "Back up and restore" — docs.n8n.io/…/backup-and-restore
- n8n docs, "Set a custom encryption key" — docs.n8n.io/…/set-a-custom-encryption-key
- n8n docs, "Configure webhook URLs with reverse proxy" — docs.n8n.io/…/configure-webhook-urls-with-reverse-proxy
- n8n docs, "Enable queue mode" (incl. multi-main) — docs.n8n.io/…/enable-queue-mode
- n8n-hosting (GitHub), example docker-compose/withPostgres — github.com/n8n-io/n8n-hosting
- n8n releases on GitHub (2.0.0 — 08.12.2025; 2.41.6 — 02.10.2026; beta 2.42.2 — 01.10.2026) — github.com/n8n-io/n8n/releases
- n8n, Sustainable Use License and "Community license" in the documentation — github.com/n8n-io/n8n … LICENSE.md · docs.n8n.io/…/community-license
- Tailscale Docs, "Tailscale Serve" — tailscale.com/kb/1312/serve
- Tailscale Docs, "Tailscale Funnel" — tailscale.com/kb/1223/funnel
Checked on 02.10.2026. n8n versions change almost every week; check the current one before you copy it.
10 · RELATEDContinue from here
n8n: Docker install and your first workflow
Step by step, from the first start to a working process.
Lesson · AcademyTailscale: subnet router, sharing, keys
How to set up the internal-network access we talk about above.
Step · The LadderThe Ladder
The steps with which we can go through the setup for your environment together.