FlowKit

Installing n8n with Docker: the complete guide (compose, Postgres, production)

Published 27 July 2026 · 7 min read

Three ways to self-host n8n show up across the docs and forums: npm directly on the host, Docker, or a Kubernetes chart for large deployments. For a single instance on a VPS — which covers the vast majority of installs — Docker is the recommended route, and also the easiest to maintain over time. This guide walks through the full setup: prerequisites, a commented docker-compose.yml with PostgreSQL, first start, and the checklist that separates "it runs on my server" from an instance that's genuinely production-ready. Still weighing self-hosting against a subscription? Our self-hosted vs n8n Cloud comparison covers that decision first.

Why Docker is the recommended method

Installing n8n via npm works, but it ties the application to the system's Node.js version, its libraries, and whatever else lives on the machine. The first OS or Node upgrade can break the install for reasons that have nothing to do with n8n. Docker removes that entire class of problems: the official image ships the exact Node.js version and dependencies the n8n team tests against, and the environment is identical on your VPS, on your laptop, and in the documentation's examples.

That's not a marketing claim but an engineering principle documented since Docker's earliest days: Dirk Merkel's foundational article, Docker: lightweight Linux containers for consistent development and deployment (Linux Journal, 2014 — see it on Google Scholar), presented the container precisely as the answer to "it worked on my machine". On the performance side, the study by Felter, Ferreira, Rajamony and Rubio published at IEEE ISPASS 2015 (Google Scholar) measured that Linux containers introduce overhead equal to or lower than virtual machines, close to native performance in most cases — the isolation doesn't cost you workflow responsiveness.

What this buys you day to day with n8n:

  • Controlled updates: change one version number in a file, run docker compose up -d, and rolling back stays possible as long as the database was backed up.
  • The whole stack in one file: n8n + PostgreSQL (+ Redis later for queue mode) described in a single docker-compose.yml you can version in Git.
  • Isolation: n8n doesn't pollute the host system, and vice versa.

Prerequisites

Item Recommendation
Server Linux VPS (Debian/Ubuntu), 2GB RAM as a comfortable baseline, at least 1 vCPU
Software Docker Engine + the Docker Compose plugin (v2, the docker compose command)
Network A domain name pointing at the VPS if webhooks need to be reachable from the internet

On RAM: n8n boots with less, but the moment a workflow handles attachments, parses large JSON payloads, or chains AI model calls, the headroom disappears fast. The real cost of self-hosted n8n — VPS included — is very reasonable; there's no point saving $3 a month on memory.

Installing Docker on a fresh Debian/Ubuntu VPS:

# Installs Docker Engine + the compose plugin via the official script
curl -fsSL https://get.docker.com | sh

# Checks that the compose v2 plugin responds
docker compose version

If docker compose version prints a version, you're set. Avoid the old docker-compose binary (with a hyphen) still found in some package repos: that's v1, which is deprecated.

The minimal docker-compose.yml (n8n + PostgreSQL)

Create a dedicated directory (say /opt/n8n) and drop this docker-compose.yml into it:

volumes:
  n8n_data:        # n8n data: encryption key, config, community nodes
  postgres_data:   # PostgreSQL data

services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=change-this-password
      - POSTGRES_DB=n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      # n8n should only start once the database accepts connections
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 5s
      timeout: 5s
      retries: 10

  n8n:
    # In production, replace "latest" with an exact version (see checklist)
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      # Public identity of the instance
      - N8N_HOST=n8n.yourdomain.com
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      # Timezone used by scheduled triggers (Cron, Schedule)
      - GENERIC_TIMEZONE=Europe/Paris
      - TZ=Europe/Paris
      # Connect to PostgreSQL instead of the default SQLite
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=change-this-password
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

Three points deserve a closer look:

  • WEBHOOK_URL is the most commonly forgotten variable. It determines the URL n8n displays (and registers with third-party services) for your webhooks. Without it, behind a reverse proxy, n8n generates webhook URLs that nothing can reach.
  • GENERIC_TIMEZONE drives when scheduled triggers fire. Without it, a "every day at 8am" workflow runs at 8am UTC, not 8am in your timezone.
  • The n8n_data volume mounted at /home/node/.n8n holds, among other things, the credentials encryption key generated on first start. Lose that volume without having noted the key, and every saved credential becomes undecryptable — the details are in our guide to securing API credentials in n8n.

Why PostgreSQL over the default SQLite

Without the DB_* variables, n8n uses SQLite: a single database file inside the n8n_data volume. Perfect for exploring the tool, and clearly insufficient once the instance becomes a production dependency:

Criterion SQLite (default) PostgreSQL
Getting started Zero configuration One extra service in the compose file
Large execution history The file grows, performance degrades Built for that write volume
Hot backups Tricky (the file may be mid-write) Clean, scriptable pg_dump
Queue mode (multiple workers) Not supported Required

Migrating from SQLite to PostgreSQL after the fact means moving data across: better to start on PostgreSQL directly, since the extra cost in the docker-compose.yml is about fifteen lines. Backing up and restoring that database is covered in depth in our PostgreSQL backup and restore guide for self-hosted n8n.

First start and creating the owner account

From the directory containing the docker-compose.yml:

docker compose up -d        # starts postgres, then n8n, in the background
docker compose logs -f n8n  # follow the logs until "Editor is now accessible"

Then open http://your-server-ip:5678 (or your domain once the proxy is in place). On first access, n8n prompts you to create the owner account: the instance's primary administrator, who manages users and settings. Use a real email address and a strong password — this step only happens once, while the database is empty.

Two quick checks confirm the install:

# The database in use really is PostgreSQL (no database.sqlite file should exist)
docker compose exec n8n ls /home/node/.n8n

# Both containers are "running" / "healthy"
docker compose ps

Production checklist

An instance answering on port 5678 is not a production instance. Four workstreams, in this order:

1. HTTPS and a domain name. Don't leave the editor and your webhooks on plain HTTP over a public IP: put a reverse proxy in front of n8n to terminate TLS with a Let's Encrypt certificate, and stop publishing port 5678 to the outside. The full setup, with Traefik or Caddy and the variables to adjust (N8N_PROTOCOL, WEBHOOK_URL), is described in our guide to running n8n over HTTPS with a domain name.

2. Backups. Two things to back up, always together: the PostgreSQL database (a scheduled pg_dump) and the n8n_data volume — or at minimum the encryption key it contains. A database backup without the encryption key restores credentials that can't be read. The backup and restore guide provides the scripts and a restore-test procedure.

3. Pinned updates. Replace latest with an exact version (docker.n8n.io/n8nio/n8n:1.x.y, taken from the official releases) as soon as the instance carries real workflows. With latest, a routine docker compose pull on the wrong day can jump several major versions at once, database migrations included. The proper procedure — back up, read the release notes, upgrade incrementally — is laid out in updating n8n on Docker without breaking anything.

4. Monitoring. restart: unless-stopped restarts n8n if it crashes, but it doesn't tell you it crashed, or that the disk is filling up, or that executions are failing in a row. An external healthcheck on /healthz plus a few basic alerts are covered in our guide to monitoring an n8n instance.

What's next?

The instance is up, on HTTPS, backed up, pinned to a known version: time to fill it. You can build your first workflows by hand — our guide to getting started with n8n's AI nodes is a good entry point — or import ready-made ones: the AI Inbox Pack ($79), for instance, gives you automatic email triage and reply drafts, importable in minutes on a freshly installed instance. That's often the fastest way to make self-hosting pay for itself in the first week: the infrastructure described in this guide is exactly what those workflows need to run.

FAQ

Frequently asked questions

What server specs do I need to install n8n with Docker?

A Linux VPS with 2GB of RAM is a comfortable baseline for an n8n instance with PostgreSQL and a few dozen workflows. It will start with less, but workflows that handle file attachments or chain AI model calls will quickly eat through the headroom. For typical automation workloads, RAM matters more than CPU.

Do I really need PostgreSQL, or is SQLite enough?

For trying n8n out on your own machine, SQLite (the default database) is perfectly fine. As soon as the instance matters in production — running continuously, accumulating execution history, needing real backups — PostgreSQL is the right call: it handles concurrent writes better, backs up cleanly with pg_dump, and is required for queue mode with multiple workers.

How do I update n8n when it's installed with Docker?

Pin an exact version in your docker-compose.yml instead of the latest tag, back up the database and the n8n_data volume, bump the version number, then run docker compose pull and docker compose up -d. n8n applies database migrations automatically on startup, which makes rolling back tricky without a prior backup.

Should port 5678 be exposed directly to the internet?

Not in production. Port 5678 serves unencrypted HTTP: best practice is to put a reverse proxy (Traefik, Caddy, Nginx) in front of n8n to terminate TLS on a dedicated domain name, and to stop publishing port 5678 beyond the machine itself. The editor and your webhooks then go through HTTPS only.

Bundle FlowKit Complet

€269