How to Self-Host n8n with Docker Compose

If you’re searching for “n8n docker compose”, you probably want a reproducible way to run n8n that survives restarts and upgrades.

This tutorial sets up n8n using Docker Compose with:

  • persistent data storage (so workflows/users don’t disappear)

  • a predictable upgrade path

  • a simple way to expose it on your Zo

What you’ll end up with

  • n8n running in Docker

  • a docker-compose.yml you can version-control

  • a data volume for n8n state

Prerequisites

  • A Zo (or any Linux server) where you can run commands in a terminal

  • Docker installed

    • On Zo: if Docker isn’t installed yet, ask your AI to “install Docker and Docker Compose”, then continue

Step 1: Create a project folder

In your terminal:

mkdir -p ~/services/n8n
cd ~/services/n8n

Step 2: Create a Docker Compose file

Create docker-compose.yml:

services:
  n8n:
    image: n8nio/n8n:latest
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=localhost
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - WEBHOOK_URL=http://localhost:5678/
      - GENERIC_TIMEZONE=UTC
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:

Notes:

  • n8n_data is where n8n stores users, credentials, workflows, and execution data.

  • Using a named volume keeps your state separate from the container image, so you can upgrade safely.

Step 3: Start n8n

docker compose up -d

Check that it’s running:

docker compose ps

View logs if something looks off:

docker compose logs -f --tail=200

Step 4: Open n8n in your browser

  • If you’re on Zo: visit http://localhost:5678 from within Zo’s browser environment.

  • If you need a public URL: expose port 5678 as a Zo Service (HTTP) and use the public endpoint it gives you.

Step 5: Set a real webhook URL (important)

Many n8n integrations rely on webhooks. If you access n8n via a public URL, update WEBHOOK_URL to match.

Example (replace with your real endpoint):

- WEBHOOK_URL=http://YOUR_PUBLIC_URL/

Then restart:

docker compose up -d

Step 6: Basic security you should not skip

If your n8n is reachable from the public internet:

  1. Put it behind authentication.

  2. Prefer HTTPS.

  3. Restrict access where possible.

A minimal first step is enabling basic auth:

- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=change-me
- N8N_BASIC_AUTH_PASSWORD=change-me-too

After changing environment variables, restart the container:

docker compose up -d

Step 7: Upgrading n8n

docker compose pull
docker compose up -d

Your workflows persist because they’re stored in the volume.

Troubleshooting

n8n starts but can’t log in / missing workflows

  • Make sure the volume is attached (n8n_data:/home/node/.n8n).

  • Don’t delete volumes unless you intend to wipe state.

Port 5678 already in use

Change the host port:

ports:
  - "5679:5678"

Then restart.

Summary

You now have a clean Docker Compose setup for n8n with persistent storage and a straightforward upgrade process. If you want to productionise it further, the next steps are: HTTPS + a stable hostname + tighter access controls.