n8n is one of the most popular workflow automation tools, and one of its biggest advantages is that you can host it yourself. That gives you full control over your data, no per-execution fees and the freedom to connect internal systems.
This guide shows how to set up a production-ready self-hosted n8n instance with Docker Compose: HTTPS, a PostgreSQL database, correct webhook URLs, backups and updates. If you are still choosing a tool, read my n8n vs Zapier vs Make comparison first.
What you need
- A Linux VPS (for example Ubuntu LTS) with at least 2 GB RAM for small workloads, more for heavy or AI workflows
- A domain or subdomain, for example
n8n.example.com, pointing to the server’s IP - Docker and the Docker Compose plugin installed
- Basic comfort with the command line
Step 1: Install Docker
On Ubuntu, follow Docker’s official installation instructions for Docker Engine and the Compose plugin. Then check:
docker --version
docker compose version
Step 2: Quick test run (optional)
To see n8n running in a minute:
docker volume create n8n_data
docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n
Open http://SERVER-IP:5678. This is fine for testing, but not for production: no HTTPS, SQLite database and no automatic restarts.
Step 3: Create the project folder
mkdir -p ~/n8n && cd ~/n8n
Step 4: Create the .env file
DOMAIN=n8n.example.com
GENERIC_TIMEZONE=Europe/Amsterdam
POSTGRES_USER=n8n
POSTGRES_PASSWORD=change-this-strong-password
POSTGRES_DB=n8n
N8N_ENCRYPTION_KEY=change-this-long-random-string
Generate a strong encryption key, for example with openssl rand -hex 32.
Important: n8n uses N8N_ENCRYPTION_KEY to encrypt stored credentials. If you lose it, you lose access to every saved credential. Store it in your password manager.
Step 5: Create docker-compose.yml
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- db_data:/var/lib/postgresql/data
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_HOST=${DOMAIN}
- N8N_PROTOCOL=https
- N8N_PORT=5678
- WEBHOOK_URL=https://${DOMAIN}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
volumes:
db_data:
n8n_data:
A few notes:
- Port 5678 is bound to
127.0.0.1only, so n8n is reachable only through the reverse proxy WEBHOOK_URLmakes sure webhook URLs shown in n8n use your public HTTPS domain- In production, pin the n8n image to a specific version tag instead of
latest, so updates happen when you choose
Step 6: Add HTTPS with a reverse proxy
The simplest option is Caddy, which gets and renews Let’s Encrypt certificates automatically. Install Caddy on the host and use this Caddyfile:
n8n.example.com {
reverse_proxy 127.0.0.1:5678
}
Nginx with Certbot or Traefik works just as well. Make sure WebSocket connections are passed through, the n8n editor needs them.
Step 7: Start n8n
docker compose up -d
docker compose logs -f n8n
Open https://n8n.example.com and create the owner account.
Step 8: Secure the server
- Allow only ports 22, 80 and 443 in the firewall (for example with
ufw) - Use SSH keys and disable password login
- Keep the OS and Docker updated
- Restrict who can create accounts in n8n and enable two-factor authentication where available
- Be careful with nodes that can run code or commands, especially if several people use the instance
Step 9: Back up n8n
You need to back up two things: the database and the encryption key (your .env file).
Database backup:
docker compose exec -T postgres pg_dump -U n8n n8n | gzip > n8n-$(date +%F).sql.gz
You can also export workflows and credentials with the n8n CLI:
docker compose exec n8n n8n export:workflow --all --output=/home/node/.n8n/backup-workflows.json
Copy backups off the server. A backup on the same server is not a backup. My server backup guide explains a simple, reliable setup.
Step 10: Update n8n safely
docker compose pull
docker compose up -d
Before major updates:
- Read n8n’s release notes for breaking changes
- Take a database backup
- Test important workflows after the update
Scaling: queue mode
For high volumes, n8n supports queue mode: the main instance receives triggers and webhooks, Redis holds a queue, and separate worker containers execute workflows. Start simple, and move to queue mode when executions start to wait or the server is constantly busy.
Troubleshooting
Webhook URLs show localhost
Set WEBHOOK_URL to your public HTTPS URL and restart.
“Connection lost” in the editor
Usually the reverse proxy does not pass WebSocket connections. Check your proxy configuration.
Credentials cannot be decrypted after moving servers
The N8N_ENCRYPTION_KEY on the new server is different. Use the original key.
Wrong times in schedules
Set GENERIC_TIMEZONE and TZ to your time zone.
Frequently asked questions
Is self-hosted n8n free?
The software can be self-hosted under n8n’s license; some features are reserved for paid plans. You pay for your server and the time to maintain it. Check n8n’s current license terms for your use case.
SQLite or PostgreSQL?
SQLite works for testing and small setups. PostgreSQL is more robust for production, larger execution histories and queue mode.
How much server do I need?
Small instances run on 2 GB RAM. Workflows that process large files, many items or AI calls need more. Monitor memory and scale up when needed.
Want n8n set up properly?
I set up and maintain self-hosted n8n instances and build the workflows on top: store, CRM, email, ERP and AI integrations. See my marketing automation service and servers and backups service, or request help.