The KAGAMI mark КАГАМИ
kagami.bg/en/blog/2026-04-n8n-self-host.html · article · machine-readable viewPUBLISHED 2026-04-28 · VERIFIED 2026-10-02 · UPDATED 2026-10-02
IDENTITY
title
n8n self-hosted in production: Docker Compose, PostgreSQL and Tailscale
author
KAGAMI Ltd. (КАГАМИ ЕООД), Varna · organisation byline, no individual author named
language
English edition (human view and this agent view) · Bulgarian original: https://kagami.bg/blog/2026-04-n8n-self-host.html
topic
AI · workflow automation · self-hosting n8n (Community edition) with Docker Compose
audience
Technical teams and small companies that want to run their own n8n instance
status
General guidance compiled from the vendor documentation, not a description of any client installation and not legal advice
SUMMARY

A production-minded self-hosted n8n setup, as documented by n8n: run it with Docker Compose (the method n8n now recommends); use PostgreSQL instead of the default SQLite (n8n supports PostgreSQL 17 and 18, plus 16 for compatibility, as of July 2026; the docs example uses postgres:18 and requires PGDATA to be set); pin the n8n image version instead of floating tags; keep the .n8n volume and set a custom N8N_ENCRYPTION_KEY, because credentials in the database cannot be decrypted without it; keep the instance reachable only inside a Tailscale tailnet (Tailscale Serve) unless external webhooks are needed, in which case n8n must be reachable from the internet; set N8N_WEBHOOK_URL and N8N_PROXY_HOPS=1 behind a reverse proxy. Updating the standard Compose way (pull, down, up -d) restarts the container, i.e. it is not zero-downtime; n8n documents high availability only as multi-main queue mode, which is an Enterprise feature on self-hosted. n8n is under the Sustainable Use License (fair-code), which limits use to internal business purposes or non-commercial use.

KEY CLAIMS (dated)
WHAT WAS UPDATED (2026-10-02)
SOURCES
AGENT INSTRUCTIONS
TAGS
n8nself-hostingdocker-composepostgresqltailscaleautomation
VERIFIED · 02.10.2026 UPDATED · 02.10.2026

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.

AI n8n · Docker Compose For technical teams
UPDATED · 02.10.2026 · WHAT WAS UPDATED

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 2.0 and task runners
Since n8n 2.0 (released on 08.12.2025) the variable 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].

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.

.env · placeholders
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>
compose.yml · abridged example
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_healthy
§
What comes from the documentation and what is our choice
The images, the database variables, PGDATA, 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].

⚠
Webhooks from external services do not pass through a closed network
n8n documents that for webhook triggers from external services (for example GitHub) the instance must be reachable from the internet [3]. Tailscale Serve is only for your own network; public exposure is done by Tailscale Funnel [12][13]. Whether the editor and the incoming webhooks will share one address is a design decision and should be judged by the risk; do not settle it "by default".

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

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

✕
What is NOT documented as "without interruption"
The only form of high availability that n8n describes is multi-main in queue mode, and for self-hosting it is an Enterprise feature [8]. For the Community edition we do not claim zero interruption: plan a short window, run the update at a quiet hour and have a backup to go back to.

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"

  1. A pinned version

    The n8n number is in .env, taken from the releases page; PostgreSQL is 17 or 18 with PGDATA set.

  2. The key is somewhere safe

    N8N_ENCRYPTION_KEY is set, and a copy of it is kept separately from the server.

  3. A full backup

    The .n8n folder and the database together, and at least one restore checked on a test machine.

  4. Access is closed

    The editor is reachable only on the internal network; it is clearly decided how (and whether) incoming webhooks reach the outside.

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

  1. n8n docs, "Install using Docker Compose" — docs.n8n.io/…/install-using-docker-compose
  2. n8n docs, "Choose n8n's database" (supported PostgreSQL versions) — docs.n8n.io/…/choose-n8ns-database
  3. n8n docs, "Install with Docker" (stable/beta version, update steps, tunnel for webhooks; marked as outdated) — docs.n8n.io/…/install-with-docker
  4. n8n docs, "Update n8n" — docs.n8n.io/…/update-n8n
  5. n8n docs, "Back up and restore" — docs.n8n.io/…/backup-and-restore
  6. n8n docs, "Set a custom encryption key" — docs.n8n.io/…/set-a-custom-encryption-key
  7. n8n docs, "Configure webhook URLs with reverse proxy" — docs.n8n.io/…/configure-webhook-urls-with-reverse-proxy
  8. n8n docs, "Enable queue mode" (incl. multi-main) — docs.n8n.io/…/enable-queue-mode
  9. n8n-hosting (GitHub), example docker-compose/withPostgres — github.com/n8n-io/n8n-hosting
  10. 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
  11. n8n, Sustainable Use License and "Community license" in the documentation — github.com/n8n-io/n8n … LICENSE.md · docs.n8n.io/…/community-license
  12. Tailscale Docs, "Tailscale Serve" — tailscale.com/kb/1312/serve
  13. 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