Self-host n8n on a VPS with HTTPS
Run n8n in Docker on your own server, behind Caddy with a free HTTPS certificate, with its data in a volume you can back up and an easy way to update.
To self-host n8n, run its official Docker image with Docker Compose, keep its port on localhost, and put Caddy in front to serve it over HTTPS on your own domain. It takes about fifteen minutes on a fresh Ubuntu 24.04 or Debian 12 server with 2 GB of memory or more.
HTTPS isn't optional here. n8n stores the passwords and API keys your workflows use, and webhooks from other services expect an https:// address.
What you need
- A server running Ubuntu 24.04 or Debian 12 or 13. Commands below are run as root.
- A domain or subdomain you can point at it, like
n8n.example.com. - Docker with the Compose plugin. If it isn't installed, Install Docker takes five minutes.
Point the domain at the server
At your DNS provider, add an A record for n8n.example.com with your server's IPv4 address, for example 203.0.113.10. Add an AAAA record for its IPv6 address too, once IPv6 works on the server. Caddy needs DNS in place to get a certificate, so do this first.
Check it from the server:
getent hosts n8n.example.comOpen the web ports
Caddy needs ports 80 and 443. With ufw:
ufw allow 80/tcp
ufw allow 443/tcpn8n's own port, 5678, stays closed. Docker can open ports behind ufw's back (more on that in Install Docker), which is why the Compose file below binds it to 127.0.0.1 only.
Run n8n with Docker Compose
Make a directory for it:
mkdir -p /opt/n8n
cd /opt/n8nCreate /opt/n8n/compose.yaml, with your own domain and time zone:
services:
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.example.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- N8N_PROXY_HOPS=1
- NODE_ENV=production
- N8N_WEBHOOK_URL=https://n8n.example.com/
- WEBHOOK_URL=https://n8n.example.com/
- GENERIC_TIMEZONE=Europe/London
- TZ=Europe/London
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:What the settings do:
N8N_HOSTandN8N_PROTOCOLtell n8n its public address, so the links it shows you are right.N8N_WEBHOOK_URLis the address it gives other services for webhooks. Without it, they gethttp://localhost:5678/..., which nobody outside can reach. Versions of n8n before 2.35 only read the older name,WEBHOOK_URL, so we set both; newer ones accept it too but log a deprecation warning, which you can ignore or silence by dropping that line.N8N_PROXY_HOPS=1says one proxy, Caddy, sits in front, so n8n trusts the visitor address Caddy passes on.GENERIC_TIMEZONEis the time zone for schedule triggers. Use a name fromtimedatectl list-timezones.- The
n8n_datavolume holds everything: the database, settings, and the encryption key for your saved credentials.
Start it:
docker compose up -d
docker compose logs -fWait for a line saying the editor is accessible, then press Ctrl+C. curl -I http://127.0.0.1:5678 should get an answer.
Put Caddy in front for HTTPS
Caddy gets and renews a Let's Encrypt certificate on its own. Install it from Caddy's repository:
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install -y caddyReplace everything in /etc/caddy/Caddyfile with:
n8n.example.com {
reverse_proxy 127.0.0.1:5678
}Then:
systemctl reload caddy
journalctl -u caddy -n 30The log should show a certificate being obtained. Caddy passes websockets through without extra settings, which the n8n editor needs.
If you already run nginx on this server, use it instead of Caddy: proxy to 127.0.0.1:5678, and add the Upgrade and Connection headers for websockets. Serve a site with nginx and HTTPS covers the certificate.
Create the owner account straight away
Open https://n8n.example.com. The first person to load the page sets up the owner account, so do it now, with a strong password. Then turn on two-factor login under your user's Settings.
Back it up
Everything that matters is in the n8n_data volume. Compose names it after the directory, so it's n8n_n8n_data. To take a copy:
cd /opt/n8n
docker compose stop
docker run --rm -v n8n_n8n_data:/data -v /root:/backup alpine tar czf /backup/n8n-$(date +%F).tgz -C /data .
docker compose startThe encryption key is in that archive (in the config file). Without it, a restored n8n can't decrypt your saved credentials, so never back up the database without it. It's also worth exporting your workflows to a file from time to time, one you can keep in Git:
docker compose exec n8n n8n export:workflow --all --output=/home/node/.n8n/workflows.jsonCopy the archives off the server. Backups you should run shows how to do it nightly.
Update
n8n releases often. Back up first, then:
cd /opt/n8n
docker compose pull
docker compose up -d
docker image prune -fRead the release notes before a major version jump. To choose when you upgrade, pin an exact version on the image, like docker.n8n.io/n8nio/n8n:2.44.3, instead of the default latest (n8n doesn't publish a tag for a whole major version), and change it when you're ready.
When it goes wrong
- The browser shows a certificate warning or Caddy can't get one: DNS isn't pointing at the server yet, or port 80 is closed. Check
getent hosts n8n.example.comandufw status. - Webhooks show
localhostURLs:N8N_WEBHOOK_URLis missing or the container wasn't recreated. Rundocker compose up -dafter any change tocompose.yaml. - "Connection lost" in the editor: websockets aren't getting through. Caddy handles them; with nginx, check the
Upgradeheaders. - Credentials can't be decrypted after a restore: the
configfile with the encryption key wasn't restored with the rest.
Doing it on VPSNine
The n8n one-click app installs Docker and runs n8n with its data in a named volume on the first boot. You still add your domain and HTTPS, as above, before storing credentials in it.
n8n needs at least the Standard plan, with 4 GB of memory, so it has room alongside Docker and your workflows' data. The plans page has the specs. If you want to run n8n next to other apps with a web UI to manage them, Coolify can deploy it for you.