Guides · Automation

Self-host n8n with Docker on a VPS

Run the n8n workflow tool on your own server using n8n’s Docker Compose setup, with Traefik handling HTTPS. Written for Ubuntu and checked against n8n’s and Docker’s documentation.

Is self-hosting right for you? n8n says self-hosting needs experience with servers, containers and security, and recommends its paid n8n Cloud if you’re not comfortable with that. You are responsible for updates and backups.

What you need

  • A VPS with Ubuntu 22.04, 24.04 or 26.04 (64-bit). 1 vCPU and 2 GB RAM is a reasonable start for light use; more for heavy workflows.
  • A domain or subdomain you can point at the server.
  • Ports 80 and 443 open in your provider’s firewall.
  • Some hosts, including Hostinger, offer an n8n template that does these steps for you.

Install Docker Engine

Use Docker’s own apt repository rather than the distro’s docker.io package:

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker --version
docker compose version

Firewall warning from Docker’s docs: ports you publish from a container bypass ufw rules. The Compose file below only publishes 80 and 443 publicly and binds n8n’s port 5678 to 127.0.0.1.

Point a subdomain at the server

Create an A record such as n8n pointing to your server’s IP address. Wait for it to resolve before starting, so Traefik can get a Let’s Encrypt certificate.

Create the Compose files

mkdir n8n-compose && cd n8n-compose
mkdir local-files

Create .env with your own values:

DOMAIN_NAME=example.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=Asia/Manila
SSL_EMAIL=you@example.com

SSL_EMAIL is the address Let’s Encrypt uses for certificate notices; use your own.

Create compose.yaml. This is n8n’s published configuration; check their page for updates before you use it.

services:
  traefik:
    image: "traefik"
    restart: always
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
      - "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
      - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - traefik_data:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro

  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    labels:
      - traefik.enable=true
      - traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
      - traefik.http.routers.n8n.tls=true
      - traefik.http.routers.n8n.entrypoints=web,websecure
      - traefik.http.routers.n8n.tls.certresolver=mytlschallenge
      - traefik.http.middlewares.n8n.headers.SSLRedirect=true
      - traefik.http.middlewares.n8n.headers.STSSeconds=315360000
      - traefik.http.middlewares.n8n.headers.browserXSSFilter=true
      - traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
      - traefik.http.middlewares.n8n.headers.forceSTSHeader=true
      - traefik.http.middlewares.n8n.headers.SSLHost=${DOMAIN_NAME}
      - traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
      - traefik.http.middlewares.n8n.headers.STSPreload=true
      - traefik.http.routers.n8n.middlewares=n8n@docker
    environment:
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - N8N_WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${GENERIC_TIMEZONE}
      - N8N_RESTRICT_FILE_ACCESS_TO=/files
    volumes:
      - n8n_data:/home/node/.n8n
      - ./local-files:/files

volumes:
  n8n_data:
  traefik_data:

N8N_WEBHOOK_URL needs n8n 2.30.0 or newer; older versions use WEBHOOK_URL.

Start and update

sudo docker compose up -d
# later, to update to the newest image:
sudo docker compose pull
sudo docker compose up -d

Open https://n8n.example.com (your own subdomain) and create the owner account straight away, before anyone else finds the URL.

Keep it safe

  • Back up the n8n_data volume: it holds the database and the encryption key for your credentials.
  • Keep Ubuntu and the images updated, and use SSH keys for server logins.
  • Allow only ports 22, 80 and 443 in your provider’s cloud firewall.
VPS plans that fit this guide:Hostinger VPSVultr pricing

Sources (checked 13 Nov 2026): n8n Docker Compose docs, Docker Engine on Ubuntu.