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

## Key facts

| Fact | Value |
|---|---|
| App | n8n (https://appsgit.com/apps/n8n) |
| Difficulty | beginner |
| Time | about 15 minutes |
| Requirements | 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) |
| Last updated | 2026-10-06 |

## 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](https://docs.docker.com/engine/install/ubuntu/). Then create a folder for the stack:

```bash
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`:

```yaml
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:

```bash
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](https://github.com/n8n-io/n8n/releases).

## Step 3: Start and open the app

```bash
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](https://caddyserver.com/docs/):

```caddyfile
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:

```bash
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.

## FAQ

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

Prefer not to do it yourself? [appsgit installation help](https://appsgit.com/services/install) installs it on your server for a fixed quote.

---

Canonical page: https://appsgit.com/guides/n8n
Source: appsgit (https://appsgit.com), the app store for github. Data from the GitHub API, refreshed nightly.
Machine access: JSON API https://appsgit.com/api/v1/apps (OpenAPI: https://appsgit.com/openapi.json), MCP server https://mcp.appsgit.com/mcp, full index https://appsgit.com/llms-full.txt.
