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_datavolume: 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.
Sources (checked 13 Nov 2026): n8n Docker Compose docs, Docker Engine on Ubuntu.