How to self-host n8n with Docker Compose, Postgres and HTTPS

By Wolfgang Wachelhofer · · 10 min read

To self-host n8n, run the free Community edition on a Linux server with Docker Compose: n8n and PostgreSQL behind a reverse proxy that handles HTTPS. Set the webhook URL and a fixed encryption key, publish only ports 80 and 443, back up the database and the .n8n folder, and update at least once a month.

Key takeaways

  • The self-hosted Community edition is free under the Sustainable Use License, for internal business, personal and non-commercial use.
  • n8n Cloud starts at €20 a month billed annually for 2,500 executions; self-hosted, you pay only for the server.
  • Behind a reverse proxy, set N8N_WEBHOOK_URL (or WEBHOOK_URL before n8n 2.30.0) or your webhooks point at the wrong address.
  • Without the encryption key from the .n8n folder, a restored database can’t decrypt your credentials. Back up both.
On this page
  1. n8n Cloud vs self-hosted: which should you pick?
  2. What do you need to self-host n8n?
  3. Why use Postgres instead of SQLite?
  4. n8n Docker Compose setup with Postgres and HTTPS
  5. How to back up self-hosted n8n
  6. How to update n8n in Docker
  7. What’s the cheapest way to host n8n?
  8. Self-host n8n with Nuvlo instead

n8n Cloud vs self-hosted: which should you pick?

n8n comes in two forms. n8n Cloud is run by n8n: you sign up and build workflows, and they handle servers, updates and scaling. Self-hosted n8n runs on a server you control, and you handle everything around it.

The price model is different too. Cloud plans are priced by executions, where one execution is one run of a whole workflow, however many steps it has. Self-hosted, the Community edition has no execution limit; the limit is what your server can handle.

OptionPrice (as listed by n8n)ExecutionsWho runs the server
Cloud Starter€20/month, billed annually2,500 per monthn8n
Cloud Pro€50/month, billed annually10,000 per monthn8n
Self-hosted CommunityFreeNo plan limitYou
Self-hosted Business€667/month, billed annually40,000 per monthYou
EnterpriseContact salesCustomn8n or you

Prices are from n8n’s pricing page in November 2026. Business adds SSO, Git version control, multiple environments and more shared projects. The free Community edition has almost the complete feature set, according to n8n’s guide to choosing how to use n8n.

Pick Cloud if you don’t want to look after a server at all. n8n itself says self-hosting is for people who can set up, secure and update servers. Pick self-hosted if you run many executions, need your data on your own infrastructure, or want custom nodes and settings Cloud doesn’t allow.

Tipn8n uses the fair-code Sustainable Use License, not an open-source licence. You may use it for your own internal business purposes or for personal and non-commercial use. Offering n8n to your customers so they can connect their accounts and build workflows needs a commercial agreement. See n8n’s Community license page.

What do you need to self-host n8n?

  • A Linux server with a public IP address. Ubuntu 24.04 LTS is a safe choice, and the official n8n image runs on both x86 and Arm (amd64 and arm64).
  • Docker Engine with the Compose plugin.
  • A domain or subdomain, such as n8n.example.com, with an A record pointing at the server. Many webhook senders require HTTPS.
  • Ports 80 and 443 open to the internet, and SSH for you.
  • Somewhere off the server to keep backups, such as an S3-compatible bucket.

n8n doesn’t publish a minimum server size, because memory use depends on your workflows: how much JSON and binary data each run carries, Code nodes, and how many workflows run at once. Its memory guide lists these factors. As a rule of thumb, 2 GB of RAM and 1 to 2 vCPUs are comfortable for one person or a small team running n8n and Postgres. Move up if you process large files or big batches.

Secure the server before you install anything on it. Our Ubuntu server security checklist walks through SSH keys, the firewall, fail2ban and automatic updates.

Why use Postgres instead of SQLite?

By default, self-hosted n8n stores workflows, credentials and execution history in a SQLite file inside its .n8n folder. That’s fine for trying things out. n8n’s own Docker Compose install guide says to use Postgres for a production instance with more than a handful of users or workflows running around the clock. A side benefit: you can back Postgres up with pg_dump while n8n keeps running.

n8n supports the two latest PostgreSQL major versions plus one older one; as of July 2026 that was 16, 17 and 18. The setup below uses 17.

Even with Postgres, keep the .n8n folder on a persistent volume. It holds the encryption key n8n uses for every saved credential. You can also set the key yourself with N8N_ENCRYPTION_KEY, which is what the setup below does, so it lives in your .env file as well.

n8n Docker Compose setup with Postgres and HTTPS

This setup runs three containers: Postgres, n8n, and Caddy as the reverse proxy. Caddy gets and renews Let’s Encrypt certificates on its own and passes the X-Forwarded-* headers n8n needs. n8n’s own Docker Compose guide uses Traefik for the same job; either works.

  1. Point an A record for your subdomain, for example n8n.example.com, at the server’s IP address.
  2. Install Docker Engine and the Compose plugin with Docker’s guide for Ubuntu.
  3. Create a folder for the stack and generate two secrets: a database password and an encryption key.
  4. Write the .env, compose.yaml and Caddyfile files below.
  5. Open ports 80 and 443 in the firewall and start the stack.
  6. Open https://n8n.example.com and create the owner account.
Terminal
mkdir -p ~/n8n && cd ~/n8n
openssl rand -hex 24   # database password
openssl rand -hex 32   # encryption key

Set N8N_VERSION to the latest stable release from n8n’s GitHub releases, as n8n’s install guide does. Pinning the version means n8n only changes when you change this line.

.env
N8N_VERSION=2.46.6
N8N_DOMAIN=n8n.example.com
TIMEZONE=Europe/Berlin
POSTGRES_PASSWORD=paste-the-first-secret
N8N_ENCRYPTION_KEY=paste-the-second-secret
compose.yaml
services:
  postgres:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: n8nio/n8n:${N8N_VERSION}
    restart: unless-stopped
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_PORT: 5432
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      N8N_HOST: ${N8N_DOMAIN}
      N8N_PORT: 5678
      N8N_PROTOCOL: https
      N8N_WEBHOOK_URL: https://${N8N_DOMAIN}/
      N8N_PROXY_HOPS: 1
      N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true"
      GENERIC_TIMEZONE: ${TIMEZONE}
      TZ: ${TIMEZONE}
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  db_data:
  n8n_data:
  caddy_data:
  caddy_config:
Caddyfile
n8n.example.com {
    reverse_proxy n8n:5678
}
Terminal
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
docker compose up -d
docker compose logs -f n8n

A few details matter here. N8N_WEBHOOK_URL tells n8n its public address, so webhook nodes show https://n8n.example.com/webhook/... instead of http://localhost:5678. It exists from n8n 2.30.0; on older versions use WEBHOOK_URL, which still works but logs a deprecation warning since 2.35.0. N8N_PROXY_HOPS: 1 makes n8n trust the forwarded headers from Caddy, as n8n’s reverse proxy guide recommends. GENERIC_TIMEZONE sets the timezone for Schedule triggers.

ImportantOnly Caddy publishes ports. n8n and Postgres aren’t reachable from outside, which matters because ports Docker publishes skip UFW’s rules. If you do publish n8n’s port for testing, bind it to 127.0.0.1:5678:5678, as n8n’s own example does.

How to back up self-hosted n8n

n8n’s backup guide is clear about what a complete backup is: the Postgres database and the .n8n folder with its encryption key. The CLI export of workflows and credentials is handy for moving workflows between instances, but it leaves out users, execution history, variables and settings.

Terminal
cd ~/n8n
docker compose exec -T postgres pg_dump -U n8n -d n8n | gzip > n8n-db-$(date +%F).sql.gz
docker compose cp n8n:/home/node/.n8n ./n8n-home-$(date +%F)

Run this from cron every night, then copy the files off the server, for example with rclone to an S3-compatible bucket. A backup that sits on the same disk as n8n doesn’t survive the server. Keep a copy of .env somewhere safe as well, such as a password manager, since it holds the encryption key and the database password.

To restore: stop n8n, load the dump into Postgres with psql, put the .n8n folder back, start n8n with the same .env. Test this once on a spare server before you need it.

How to update n8n in Docker

n8n ships a new minor version most weeks. Its update guide suggests updating at least once a month so you never jump many versions at once, and reading the release notes for breaking changes first. Take a backup before every update.

With the version pinned in .env, an update is three steps: change N8N_VERSION to the new release, pull the image, and recreate the containers. These are the commands n8n’s Docker guide gives:

Terminal
cd ~/n8n
nano .env            # set N8N_VERSION to the new release
docker compose pull
docker compose down
docker compose up -d

If an update goes wrong, set the old version back and restore the backup you took. Update the operating system too: unattended security upgrades cover Ubuntu’s packages, not your containers.

What’s the cheapest way to host n8n?

The cheapest reliable setup is the free Community edition on a small VPS from a budget provider, with backups going to low-cost object storage. Your only bill is the server and the storage, and there’s no per-execution price, so heavy use doesn’t raise the cost until you need a bigger server.

Cheaper still, on paper, is a server you already pay for. n8n with Postgres fits next to a few small sites if the server has spare memory. Watch memory use after you add it, since a large workflow run can push other apps out.

What self-hosting costs instead is time: setting up, patching, backups and fixing it when an update breaks something. If an hour a month of that is worth more to you than the Cloud plan, Cloud is the cheaper choice. Our comparison of VPS, shared and managed hosting goes into that trade-off.

Self-host n8n with Nuvlo instead

Nuvlo is a control panel for servers in your own cloud account. n8n is one of its 1-Click apps: it installs n8n with a PostgreSQL 16 database as two containers, on a server you already have in Nuvlo or a new one it creates at Hetzner, DigitalOcean, Vultr or another connected provider. The provider bills you for the server directly.

  • Server: pick one of your servers or New server. If the server has no Docker yet, Nuvlo installs it first; the app asks for at least 1 GB of RAM and 5 GB of disk.
  • Address: your own domain, a subdomain such as n8n.example.com (on Cloudflare, Nuvlo adds the DNS record and HTTPS), or a free temporary address with HTTPS.
  • Secrets: the database password and n8n’s encryption key are generated for you, and n8n’s webhook URL is set to the app’s address. Generated credentials are shown once, so save them.
  • Backups: back up the app’s database and data volumes to your own S3-compatible bucket, by hand or on a schedule from hourly to monthly, and restore from the dashboard. See Schedule automatic backups.
  • After install: the app’s page shows its version and status, its domains, runtime logs, and buttons to stop, start or restart it.

Every server Nuvlo builds starts with key-only SSH, a default-deny firewall, fail2ban and automatic security updates, so the hardening step above is done for you. The step-by-step guide is Install a 1-Click app.

An app counts as a site on your plan. The Free plan includes 1 server and 1 site, so n8n alone fits there; Starter is €12 a month for up to 10 servers. See pricing.

When Nuvlo is the wrong fit: if n8n is the only thing you run and you’re happy with the Compose file above, a control panel adds little. The 1-Click app is a single n8n container with its database, so if you need queue mode with separate workers, or want to pick the exact n8n release yourself, run your own Compose stack. You can still do that on a Nuvlo server, for example with the Portainer app, or compare a general-purpose Docker panel like Coolify.

Is self-hosted n8n really free?

Yes. The Community edition costs nothing and has no execution limit, under n8n’s Sustainable Use License. It allows internal business, personal and non-commercial use. You still pay for the server and backup storage, and features such as SSO, environments and Git version control need a paid Business or Enterprise licence key.

How much RAM does n8n need?

n8n doesn’t publish a minimum, because memory depends on how much data each workflow handles. For one person or a small team running n8n with Postgres, 2 GB of RAM is a comfortable start. Large files, big batches and many Code nodes need more. Out-of-memory errors show up as lost connections or heap errors in the logs.

Can I use SQLite instead of Postgres?

Yes, SQLite is n8n’s default and works for testing and light use. For production, Postgres handles many executions writing at the same time better and can be backed up while n8n runs. With SQLite you should stop n8n before copying the database file, or you risk an inconsistent backup.

Why do my n8n webhook URLs show localhost:5678?

n8n builds webhook URLs from its host, port and protocol, which behind a reverse proxy are the internal ones. Set N8N_WEBHOOK_URL to your public HTTPS address, or WEBHOOK_URL on versions before 2.30.0, and set N8N_PROXY_HOPS to 1. Restart the container and copy the webhook URL again.

How often should I update self-hosted n8n?

n8n recommends at least once a month. It releases a new minor version most weeks, and small steps are less likely to break workflows than a big jump. Back up the database and the .n8n folder first, read the release notes for breaking changes, then pull the new image and restart the stack.

Install n8n with Postgres and HTTPS on a server in your own cloud account.