// DevOps

Installing n8n on FastPanel with Docker Compose: A Clear Guide

Published on 2026-09-22

In this guide we install n8n on a server with the FastPanel control panel using Docker Compose. n8n itself runs in containers together with PostgreSQL, Redis and a separate worker, while FastPanel handles the domain, certificate and reverse proxy. The configuration mirrors the official n8n withPostgresAndWorker example from the n8n-hosting repository — with adjustments for FastPanel. Installation without a panel, behind HAProxy, is described in the article “Installing n8n in Docker with HAProxy”.

Requirements

  • A server with FastPanel installed and Docker with the docker compose plugin.
  • A domain or subdomain for n8n, e.g. n8n.example.com, with an A record pointing to the server.
  • SSH access.

A worker and Redis are needed when there are many processes and they are long-running: the main n8n process accepts webhooks and shows the editor, while execution is handled by workers. For a small installation a single n8n container with PostgreSQL is enough. How the queue mode works is covered in the article “n8n: Part 5 — Scaling”.


Step 1: Prepare the server

bash
sudo mkdir -p /opt/n8n_stack
cd /opt/n8n_stack

Step 2: Configuration files

You will need three files: docker-compose.yml, .env and init-data.sh.

2.1. .env

env
N8N_VERSION=2.40.5
N8N_DOMAIN=n8n.example.com

# PostgreSQL administrator
POSTGRES_USER=postgres_admin
POSTGRES_PASSWORD=replace-with-a-long-password
POSTGRES_DB=n8n

# separate database user for n8n
POSTGRES_NON_ROOT_USER=n8n_user
POSTGRES_NON_ROOT_PASSWORD=replace-with-a-different-password

# openssl rand -hex 32 — for each of the two keys
ENCRYPTION_KEY=replace
RUNNERS_AUTH_TOKEN=replace

ENCRYPTION_KEY encrypts credentials in the n8n database. Generate it once and store it separately from the server: without it a database backup is useless. RUNNERS_AUTH_TOKEN is the shared secret between n8n and the task runner containers that execute the Code node.

2.2. docker-compose.yml

The version: line at the start of the file is not needed: modern Docker Compose ignores it.

yaml
volumes:
  db_storage:
  n8n_storage:
  redis_storage:

x-shared: &shared
  restart: always
  image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
  environment:
    - DB_TYPE=postgresdb
    - DB_POSTGRESDB_HOST=postgres
    - DB_POSTGRESDB_PORT=5432
    - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
    - DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
    - DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
    - EXECUTIONS_MODE=queue
    - QUEUE_BULL_REDIS_HOST=redis
    - QUEUE_HEALTH_CHECK_ACTIVE=true
    - N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
    - N8N_RUNNERS_MODE=external
    - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
    - N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
    - OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
    - N8N_HOST=${N8N_DOMAIN}
    - N8N_PROTOCOL=https
    - WEBHOOK_URL=https://${N8N_DOMAIN}/
    - N8N_PROXY_HOPS=1
    - GENERIC_TIMEZONE=Europe/Moscow
    - TZ=Europe/Moscow
  volumes:
    - n8n_storage:/home/node/.n8n
  depends_on:
    redis:
      condition: service_healthy
    postgres:
      condition: service_healthy

x-runner: &runner
  restart: always
  image: n8nio/runners:${N8N_VERSION}

services:
  postgres:
    image: postgres:18
    restart: always
    environment:
      - POSTGRES_USER
      - POSTGRES_PASSWORD
      - POSTGRES_DB
      - POSTGRES_NON_ROOT_USER
      - POSTGRES_NON_ROOT_PASSWORD
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - db_storage:/var/lib/postgresql/data
      - ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - redis_storage:/data
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 5s
      retries: 10

  n8n:
    <<: *shared
    ports:
      - "127.0.0.1:5678:5678"   # n8n is published externally by FastPanel

  n8n-runner:
    <<: *runner
    environment:
      - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
      - N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
    depends_on:
      - n8n

  n8n-worker:
    <<: *shared
    command: worker
    depends_on:
      - n8n

  n8n-worker-runner:
    <<: *runner
    environment:
      - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
      - N8N_RUNNERS_TASK_BROKER_URI=http://n8n-worker:5679
    depends_on:
      - n8n-worker

What changed compared to older instructions:

  • Versions. The n8n image is from the 2.x branch instead of 1.91, PostgreSQL 18 instead of 11 — support for PostgreSQL 11 ended in 2023, while 18 is supported until November 2030. The PGDATA line is needed so Postgres 18 stores data in the mounted volume. If you already run an older version, you cannot just change the tag: first run pg_dumpall, then restore into the new version.
  • Queue mode is enabled explicitly: EXECUTIONS_MODE=queue and QUEUE_BULL_REDIS_HOST=redis. Without these lines the worker won’t receive jobs and Redis will idle.
  • Task runners. Since n8n 2.0 the Code node’s code runs in separate n8nio/runners containers: the main process and each worker have their own runner.
  • There is no custom healthcheck.js anymore: n8n has a built-in /healthz check, and for the worker it’s enabled via the QUEUE_HEALTH_CHECK_ACTIVE=true variable.

2.3. init-data.sh

The script runs once at the first PostgreSQL startup and creates a separate user for n8n:

bash
#!/bin/bash
set -e;

if [ -n "${POSTGRES_NON_ROOT_USER:-}" ] && [ -n "${POSTGRES_NON_ROOT_PASSWORD:-}" ]; then
	psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
		CREATE USER ${POSTGRES_NON_ROOT_USER} WITH PASSWORD '${POSTGRES_NON_ROOT_PASSWORD}';
		GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_NON_ROOT_USER};
		GRANT CREATE ON SCHEMA public TO ${POSTGRES_NON_ROOT_USER};
	EOSQL
else
	echo "SETUP INFO: No Environment variables given!"
fi
bash
chmod +x init-data.sh

The GRANT CREATE ON SCHEMA public line is mandatory: starting with PostgreSQL 15 a normal user cannot create tables in the public schema without explicit permission.


Step 3: Start

bash
cd /opt/n8n_stack
docker compose up -d
docker compose ps

All services should switch to running, and postgres and redis to healthy. You can check n8n itself like this:

bash
curl -sf http://127.0.0.1:5678/healthz && echo OK

Step 4: FastPanel configuration

  1. In FastPanel open Sites → Add Site and specify the domain from .env.
  2. In the site’s settings enable the reverse proxy to http://127.0.0.1:5678.
  3. Issue a Let’s Encrypt certificate for the domain and enable HTTP to HTTPS redirect.
  4. Add WebSocket support — the n8n editor keeps a persistent connection to the server. If the panel didn’t add it automatically, add the following in the additional nginx directives for this proxy:
nginx
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;

More about the panel’s capabilities in the article “Hosting Control Panels: Part 4 — FASTPANEL”.


Step 5: First login

Open https://n8n.example.com. n8n will prompt you to create the owner account — do it immediately before the address is discovered by others. There are no longer login and password environment variables: basic auth support was removed in n8n 1.0.

After logging in check the Webhook node: the webhook URL should start with https://n8n.example.com/.

Backup and upgrade

  • Backup: database dump (docker compose exec postgres pg_dump -U postgres_admin n8n > n8n.sql), the n8n_storage volume and the encryption key from .env.
  • Upgrade: make a backup, change N8N_VERSION in .env to the new version and run docker compose pull && docker compose up -d. The main process, workers and runners must run on the same version.

// Reviews

Related reviews

// Contact

Need help?

Get in touch with me and I'll help solve the problem

I reply within one business day (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirm that you are not a bot.

Write and get a quick reply