Self-hosting n8n with Docker Compose: setup, licence, backups and the parts people skip
A practical guide to running n8n on your own server with Docker Compose and HTTPS, what the Sustainable Use License allows, moving to Postgres, and backing up the encryption key you can't afford to lose.
n8n is one of the most popular tools to self-host: a visual workflow builder that can replace a stack of Zapier-style subscriptions, running on a server you control. Getting it running takes ten minutes. Running it so that an update, a disk failure or a lost key doesn't take your automations with it takes a little more thought. This guide covers both.
First: what the licence lets you do
n8n is "fair-code" under the Sustainable Use License, not a classic open-source licence. Per n8n's docs and licence FAQ:
- Allowed: using it for your own business's internal purposes, including workflows whose results your customers see (syncing your CRM to your database, sending your own notifications).
- Allowed: charging clients for building, setting up and maintaining workflows. That's consulting, and the FAQ lists it explicitly.
- Not allowed without a commercial agreement: hosting n8n as a service and letting your clients build their own workflows in it, or white-labelling it and charging for access.
If your case is unclear, n8n asks you to email license@n8n.io. Settle this before you build a product around it.
What you need
- A Linux server. Common guides put the minimum at 1 vCPU and 1 GB RAM; 2 GB is more comfortable, and you'll want more if workflows handle large files.
- A domain or subdomain with an A record pointing to the server, for example
n8n.example.com. - Docker Engine with the Compose plugin.
Many integrations (Telegram bots, WhatsApp, payment providers) only deliver webhooks to HTTPS URLs, so TLS isn't optional.
The official Docker Compose setup
n8n's own server guide uses Traefik as the reverse proxy, which obtains Let's Encrypt certificates automatically. The project folder holds a compose.yaml, a local-files directory, and an .env file:
DOMAIN_NAME=example.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=Asia/Amman
SSL_EMAIL=you@example.comTogether, SUBDOMAIN and DOMAIN_NAME give https://n8n.example.com. GENERIC_TIMEZONE matters more than it looks: it's the timezone for Cron and schedule nodes, and it defaults to New York if you leave it unset.
Key parts of the setup, as the guide describes them:
- n8n's port is published only on localhost (
127.0.0.1:5678:5678). Public traffic arrives through Traefik on 80 and 443. - A named volume
n8n_datais mounted at/home/node/.n8n. That's where n8n keeps its SQLite database and its encryption key. ./local-filesis mounted at/filesfor workflows that read or write files on the host.
Start it with docker compose up -d and open the URL to create the owner account. Do that immediately: until the owner exists, whoever reaches the page first sets it up.
If you prefer Caddy, the equivalent is a two-line site block (n8n.example.com { reverse_proxy 127.0.0.1:5678 }). Either way, set the public webhook URL environment variable so n8n generates correct webhook URLs behind the proxy. The guide notes the variable is N8N_WEBHOOK_URL from n8n 2.30.0, and WEBHOOK_URL on earlier versions.
The encryption key: back it up separately
n8n encrypts stored credentials (API keys, OAuth tokens, database passwords) with an encryption key. By default it's generated on first start and saved in the .n8n folder. If you lose it, a database backup alone won't restore your credentials; they'll be unreadable.
Two good habits:
- Set the key yourself with
N8N_ENCRYPTION_KEYin your environment, from a secrets manager, so it's documented and identical across rebuilds. - Store a copy of it somewhere other than the server.
Move from SQLite to Postgres when it matters
SQLite is fine to start with. For anything business-critical, with many executions, or with more than one person editing, switch to Postgres:
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${N8N_DB_PASSWORD}Add a postgres service to the same Compose file on an internal network, and never publish its port. Execution history grows quickly, so turn on execution data pruning so the database doesn't fill the disk.
Backups you can actually restore
Back up three things:
- The database:
pg_dump -Fcnightly (or the SQLite file, copied while n8n is stopped). - The encryption key, as above.
- Workflows as JSON, which is handy for version control and for moving between instances:
docker compose exec n8n n8n export:workflow --all --output=/files/backup/workflows.json
docker compose exec n8n n8n export:credentials --all --output=/files/backup/credentials.jsonExported credentials stay encrypted with your key unless you explicitly ask for decrypted output, so a credentials export without the key is useless. Keep them together, privately.
Ship backups off the server (S3-compatible storage, another machine) with restic or rclone, keep a couple of weeks of history, and test a restore on a scratch server at least once.
Updating without surprises
- Pin a version tag rather than tracking
latest, so updates happen when you choose. - Read the release notes for breaking changes, especially across major versions.
- The routine: back up, change the tag,
docker compose pull && docker compose up -d, then open a few critical workflows and run them.
Hardening
- Keep the editor behind HTTPS only, with strong owner credentials and two-factor authentication enabled for users.
- Firewall the server to 22, 80 and 443. Remember that Docker-published ports bypass
ufw, which is one more reason to bind n8n to127.0.0.1. - Don't expose the Postgres port, ever.
- Add an uptime monitor on the editor URL and on any critical webhook path, with alerts to a channel you actually read.
When one instance isn't enough
For heavier loads, n8n supports queue mode: the main instance receives triggers and puts executions on a Redis queue, and separate worker containers process them (EXECUTIONS_MODE=queue). Most small and mid-sized teams never need it. Postgres plus a sensibly sized server goes a long way.
Self-hosted or n8n Cloud?
Self-hosting swaps a subscription priced by executions for a fixed server bill plus your time. It's usually worth it when you run many executions, need data to stay on your infrastructure, or already operate servers. If nobody on the team will look after updates and backups, the hosted plan is the safer choice, and you can always export your workflows and move later.
Sources
- n8n: Docker Compose server setup (env variables, Traefik, volumes, ports, webhook URL variable)
- n8n: Sustainable Use License and licence FAQ
- n8n: Supported databases and settings
- n8n: Set a custom encryption key
- n8n: CLI commands (export)
- n8n: Queue mode
- n8n: Execution data pruning
Want this done for you?
Service
I can install it on your server, with SSL and backups
n8n, Supabase, Odoo or similar, on a VPS or an in-house machine: Docker, a domain with SSL, automatic backups with a restore walkthrough, and an Arabic runbook. Delivered in 2 days.