Skip to content
appsgit

Deploy guide

How to self-host n8n with Docker Compose

Run n8n on your own VPS with Docker Compose and Postgres: encryption key, webhook URL, HTTPS reverse proxy, backups and upgrades, explained step by step.

  • Updated
  • Beginner
  • About 15 minutes

You will need

  • 1 vCPU / 2 GB RAM (2 vCPU / 4 GB for heavy workflows)
  • Docker + Docker Compose v2
  • A domain name (needed for webhooks from external services)

What is n8n?

n8n is a workflow automation platform that connects APIs, databases and AI models with a visual node editor, much like Zapier or Make. Its source code is on GitHub under the Sustainable Use License, a fair-code license that lets you self-host it for free for internal use. It ships hundreds of integrations, JavaScript and Python code nodes, and AI agent building blocks.

Requirements

  • A Linux server with 1 vCPU and 2 GB of RAM to start. Give it more if you run many parallel executions.
  • Docker Engine and Docker Compose v2.
  • A domain name with a DNS record pointing at the server. Third-party services need a public HTTPS URL to call your webhooks.

Step 1: Prepare the server

The steps below assume Ubuntu 24.04 with Docker installed. If you need Docker, follow the official install guide. Then create a folder for the stack:

mkdir -p ~/n8n && cd ~/n8n

Step 2: Create the Docker Compose file

This stack runs n8n with Postgres, which the n8n team recommends for anything beyond testing. Save it as docker-compose.yml:

services:
  postgres:
    image: postgres:16-alpine
    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: 5s
      timeout: 5s
      retries: 10

  n8n:
    image: docker.n8n.io/n8nio/n8n:stable
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    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_HOST}
      N8N_PROTOCOL: https
      N8N_PORT: 5678
      WEBHOOK_URL: https://${N8N_HOST}/
      GENERIC_TIMEZONE: ${TZ}
      TZ: ${TZ}
      N8N_PROXY_HOPS: 1
      EXECUTIONS_DATA_PRUNE: "true"
      EXECUTIONS_DATA_MAX_AGE: 336
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  db_data:
  n8n_data:

Create a .env file in the same folder:

N8N_HOST=n8n.example.com
TZ=Europe/London
POSTGRES_PASSWORD=CHANGE_ME
N8N_ENCRYPTION_KEY=CHANGE_ME

Generate each secret with openssl rand -hex 32 and use a different value for each. Store a copy of N8N_ENCRYPTION_KEY in your password manager: without it, a restored database is useless because every credential is encrypted with it. WEBHOOK_URL makes n8n show and register the public address for webhook triggers, and N8N_PROXY_HOPS=1 tells it to trust the X-Forwarded-* headers from your reverse proxy. EXECUTIONS_DATA_MAX_AGE is in hours, so 336 keeps two weeks of execution history.

The stable tag follows the current stable release. To pin, replace it with an exact version from the n8n releases page.

Step 3: Start and open the app

docker compose up -d
docker compose logs -f n8n

When the log shows that the editor is accessible, finish Step 4 so you can open https://n8n.example.com. To test before DNS is ready, use an SSH tunnel: ssh -L 5678:127.0.0.1:5678 user@server, then browse to http://localhost:5678. On first load n8n asks you to create the owner account. That account has full admin rights, so use a strong password and turn on two-factor authentication in your user settings.

Step 4: Put it behind HTTPS

Port 5678 is bound to 127.0.0.1 in the Compose file, so only a reverse proxy on the same host can reach it. With Caddy:

n8n.example.com {
    reverse_proxy 127.0.0.1:5678
}

Caddy issues the certificate automatically and passes WebSocket traffic, which the editor needs for live execution updates. With Nginx Proxy Manager, create a proxy host to port 5678 and switch on "Websockets Support".

Backups and upgrades

Back up the Postgres database (docker compose exec postgres pg_dump -U n8n n8n > n8n.sql), the n8n_data volume, and your .env file with the encryption key. You can also export workflows as JSON with docker compose exec n8n n8n export:workflow --all --output=/home/node/.n8n/backup.json.

Read the release notes before a major version jump, then upgrade:

docker compose pull && docker compose up -d

Database migrations run automatically when the new container starts.

Troubleshooting

  • "Mismatching encryption keys" on start: the key in .env does not match the one saved in the n8n_data volume. Restore the original key rather than generating a new one.
  • Webhook URLs show localhost:5678: WEBHOOK_URL is missing or wrong. Fix it and run docker compose up -d to recreate the container.
  • Editor shows "Connection lost": the proxy is dropping WebSocket connections. Enable WebSocket support or check timeouts on your proxy.
  • Database grows quickly: keep execution pruning on and lower EXECUTIONS_DATA_MAX_AGE.

Next steps

Set up SMTP so you can invite team members, connect your first credentials, and import a template from the n8n workflow library. For high-volume use, look at queue mode with Redis and separate worker containers.

Spotted something out of date? Tell us and we will update the guide.

FAQ

n8n questions

Still curious? Email info@appsgit.com.

What port does n8n use?

n8n listens on port 5678 by default. Behind a reverse proxy you reach it on your HTTPS domain and keep 5678 bound to localhost.

Is self-hosted n8n free?

The self-hosted Community Edition is free to use for your own workflows. n8n is published under the Sustainable Use License, a fair-code license, so the source is open but reselling n8n as a hosted service is not allowed.

What is N8N_ENCRYPTION_KEY used for?

n8n encrypts every stored credential with this key. If you lose it or it changes, n8n can no longer decrypt your saved credentials, so set it explicitly and back it up.

Why do my n8n webhooks show localhost URLs?

n8n builds webhook URLs from WEBHOOK_URL. Set it to your public HTTPS address, for example https://n8n.example.com/, and restart the container.

Should I use SQLite or Postgres with n8n?

SQLite works for small single-user setups, but Postgres is recommended for production because it handles concurrent executions and large execution histories much better.

n8n vs Zapier?

n8n covers the same trigger-and-action automation as Zapier, adds code nodes and AI agent nodes, and has no per-task pricing when self-hosted. Zapier offers more ready-made integrations and needs no server.