To self-host n8n with Docker Compose and Nginx, run the n8n container bound to localhost on port 5678, put an Nginx server block with WebSocket headers in front of it, and add a Let’s Encrypt certificate. Then set WEBHOOK_URL and N8N_PROXY_HOPS=1 so webhooks and the editor work behind the proxy.
n8n’s own Compose guide uses Traefik, and its other server guides use Caddy. This guide is for people who already run Nginx on the server, or want to, and need the proxy settings that those guides leave out. It also covers running n8n next to another tool on the same server, backups and the errors a proxy setup tends to cause.
Tested on Ubuntu 24.04.5 LTS with n8n 2.42.4 in October 2026.
What do you need to self-host n8n?
A Linux VPS with root SSH access, a subdomain pointed at it, and about half an hour.
- A VPS running Ubuntu. The commands below are written for Ubuntu 24.04 and work the same way on Debian.
- A subdomain. Create an A record such as
n8n.yourdomain.comthat points to the server’s IP address. Do this first so DNS has updated by the time you request the certificate. - Some comfort with a terminal. n8n’s docs recommend self-hosting for people who are used to managing servers, and point everyone else to n8n Cloud. That is fair advice. You are responsible for updates, backups and security.
This guide installs n8n by itself. It does not include the separate sandbox stack that n8n’s newer Compose guide adds for its AI Assistant, which needs more memory and a privileged container.
What size VPS does n8n need?
A small one. n8n’s Hetzner guide says a small CPX11 instance is enough for most usage, and suggests 4 GB RAM and 2 vCPU if you add the n8n Assistant. What grows memory use in practice is the size of the data your workflows handle and how many run at once.
On our test server, which has 4 GB of RAM, n8n used about 353 MB of memory at idle, or 9% of the total. SerpBear was running on the same server at the time, and the two containers together used about 476 MB, roughly 12%.
Plan specs and prices below are from our Linux VPS plans page as of October 2026.
| What you run | Plan | Specs | Price |
|---|---|---|---|
| n8n alone, light workflows | Basic | 1 vCore, 3 GB RAM, 200 GB HDD | $19/mo |
| n8n plus a rank tracker or other small tools | Essential | 2 vCores, 4 GB RAM, 400 GB HDD | $25/mo |
| n8n plus a headless browser crawler | Comfort | 4 vCores, 6 GB RAM | $35/mo |
Running n8n with other tools? Order the Essential Linux VPS at $25 per month, or compare all four Linux VPS plans. VPS orders are usually delivered within 15 minutes and come with a 3-day money-back guarantee.
How do you set up n8n with Docker Compose?
Install Docker, create a project folder with a .env file and a compose.yaml file, and start the container. The Compose file below is n8n’s official one with the Traefik service and labels removed and one variable added.
Step 1: Install Docker
apt update && apt upgrade -y
curl -fsSL https://get.docker.com | sh
docker --version
docker compose version
Skip this if Docker is already on the server.
Step 2: Create the project folder
mkdir -p /opt/n8n/local-files && cd /opt/n8n
chown 1000:1000 local-files
The local-files folder is shared with the container at /files, for workflows that read or write files on disk. The chown matters when you work as root: n8n runs inside the container as user ID 1000 and cannot write to a folder that root owns.
Step 3: Create the .env file
nano .env
DOMAIN_NAME=yourdomain.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=Europe/Berlin
Replace yourdomain.com with your real domain before you save. We left the placeholder in on our own test run. n8n still starts with it, but it builds its webhook addresses from these two values, so every webhook URL would point at a domain you do not own.
Set the timezone to your own as well. n8n uses it for schedule triggers, and the default is New York if you leave it out.
Step 4: Create the compose.yaml file
nano compose.yaml
services:
n8n:
image: n8nio/n8n
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
- N8N_PROXY_HOPS=1
- 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:
The port is bound to 127.0.0.1, as in the official file. Ports that Docker publishes skip UFW rules, so binding to localhost is what keeps port 5678 off the public internet. Only Nginx on the same server can reach it.
Step 5: Start n8n
docker compose up -d
docker compose ps
docker compose logs -f n8n
Press Ctrl+C to leave the logs. Do not open n8n in a browser yet. Set up Nginx and SSL first.
How do you put n8n behind Nginx with SSL?
Create an Nginx server block that proxies to port 5678 with WebSocket support, then let Certbot add the certificate.
apt install nginx -y
nano /etc/nginx/sites-available/n8n
server {
listen 80;
server_name n8n.yourdomain.com;
client_max_body_size 16m;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
Change server_name to your own subdomain before you go on. If it does not match the address you open in the browser, Nginx serves its default “Welcome to nginx” page instead of n8n. We hit exactly that on our test run.
Enable the site and add the certificate:
ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
apt install certbot python3-certbot-nginx -y
certbot --nginx -d n8n.yourdomain.com --redirect
certbot renew --dry-run
If the firewall is not on yet, allow SSH before you enable it:
ufw allow ssh
ufw allow 'Nginx Full'
ufw enable
Now open https://n8n.yourdomain.com and create the owner account straight away. The first person to reach a new n8n instance gets to create that account, so do not leave it sitting unclaimed.
Which settings stop webhooks and the editor breaking behind a proxy?
Four of them. n8n’s docs on webhook URLs behind a reverse proxy cover the first three, and the fourth is on the Nginx side.
| Setting | Where | What goes wrong without it |
|---|---|---|
WEBHOOK_URL |
compose.yaml | n8n builds webhook URLs from its internal host and port, so they point at port 5678 and outside services cannot reach them |
N8N_PROXY_HOPS=1 |
compose.yaml | n8n does not trust the forwarded headers, so it sees every request as coming from the proxy |
X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto |
Nginx | n8n cannot tell the visitor’s address or that the original request was HTTPS |
Upgrade and Connection headers |
Nginx | The editor cannot hold its live connection and shows “Connection lost” |
Two Nginx lines in our config are choices you may want to change. client_max_body_size 16m matches n8n’s default payload limit of 16 MiB, set by N8N_PAYLOAD_SIZE_MAX. Nginx’s own default of 1 MB would reject larger webhook bodies and file uploads first. proxy_read_timeout 3600s keeps long-running requests and the editor connection from being cut after Nginx’s default 60 seconds.
After changing anything in compose.yaml or .env, apply it with docker compose up -d. A plain restart does not pick up new environment values.
To check the whole chain, add a Webhook node, click Listen for test event, and open its Test URL from another device. On our test server the request showed up in the editor straight away with x-forwarded-proto: https, the right x-forwarded-host and the caller’s real IP address in x-forwarded-for. If the editor receives the event, the WebSocket connection is working too.
Can n8n and SerpBear run on the same server?
Yes. Each runs in its own container on its own localhost port, and Nginx routes by hostname. Our test server runs both.
If you followed our guide to install SerpBear on a VPS, you already have Nginx, Certbot and the firewall in place. Skip those install lines above, add the n8n server block as a second file in sites-available, and run Certbot for the new subdomain. SerpBear stays on port 3000 and n8n on 5678, each in its own folder under /opt with its own Compose file, so updating one does not touch the other.
The pairing is useful for more than saving a server. SerpBear has an API, so an n8n workflow can pull keyword positions on a schedule and send rank changes to Slack, email or a spreadsheet.
How do you back up and update n8n?
Back up the n8n_data volume, and update with three Compose commands.
Back up the data volume
n8n’s docs describe the n8n_data volume as where it saves its SQLite database file and encryption key. The key is what decrypts your saved credentials. If you lose it, the workflows can be rebuilt but every stored credential has to be entered again, so back up the whole volume, not just an export of workflows.
Compose prefixes the volume with the folder name, so it is called n8n_n8n_data if you used /opt/n8n. Confirm with docker volume ls. Save this as /root/n8n-backup.sh:
#!/bin/sh
mkdir -p /root/backups
cd /opt/n8n
docker compose stop
docker run --rm -v n8n_n8n_data:/data -v /root/backups:/backup alpine tar czf /backup/n8n-$(date +%F).tar.gz -C /data .
docker compose start
chmod +x /root/n8n-backup.sh
Schedule it with crontab -e, for example weekly at 03:30 on Sunday:
30 3 * * 0 /root/n8n-backup.sh
n8n is stopped for a few seconds while the archive is made, so pick a time when no workflow is due. Copy the archives off the server with scp or rsync.
Update n8n
n8n’s docs say a new minor version is released most weeks. These are the update steps from the docs:
cd /opt/n8n
docker compose pull
docker compose down
docker compose up -d
Take a backup first. If you would rather choose when you upgrade, pin a version in compose.yaml, for example image: n8nio/n8n:2.42.4, the version we tested. Change the tag when you are ready to move.
What are the common problems with n8n behind Nginx?
Nearly all of them trace back to a placeholder, a missing header or a missing variable.
- The “Welcome to nginx” page instead of n8n. The
server_nameline still has the placeholder or does not match your subdomain. Fix it, then runnginx -tandsystemctl reload nginx. - “Connection lost” in the editor. The
UpgradeandConnectionheaders orproxy_http_version 1.1are missing from the Nginx block. Check that Certbot’s edits left them in place. - Webhook URLs show the wrong domain, port 5678 or http. The
.envvalues are wrong,WEBHOOK_URLis not set, or the container was not recreated. Fix the values and rundocker compose up -d. - A secure cookie warning on the login page. You opened n8n over plain HTTP or by IP address. Use the HTTPS subdomain.
- 413 Request Entity Too Large. Raise
client_max_body_sizein Nginx. For bodies above 16 MiB, raiseN8N_PAYLOAD_SIZE_MAXincompose.yamlas well. - Schedules fire at the wrong hour.
GENERIC_TIMEZONEis unset or wrong. - Cannot write to /files. The
local-filesfolder is owned by root. Runchown 1000:1000 /opt/n8n/local-files. - 502 Bad Gateway. Nginx is up but n8n is still starting or has stopped. Check
docker compose psand the logs.
Frequently asked questions
Is self-hosted n8n free?
The Community Edition costs nothing to run for your own business, and you pay only for the server. n8n is released under the Sustainable Use License, which is source-available and not an open-source license. Read it before you offer n8n to others as a hosted service.
Do I need PostgreSQL to run n8n?
No. n8n’s Compose guide uses the built-in SQLite database, which is what this guide sets up. PostgreSQL is supported and is the usual step up when workloads get heavy.
Why use Nginx when n8n’s docs use Traefik?
Either works. Nginx makes sense when it is already on the server for other sites or tools, because one proxy then handles every subdomain and certificate. If n8n is the only thing on the server, the official Traefik file is less to set up.
Can I open n8n by IP address without a domain?
Not with this setup, and we would not expose it that way. For a private test, tunnel the port over SSH with ssh -L 5678:localhost:5678 root@your-server-ip and browse to http://localhost:5678.
How much RAM does n8n use?
On our test server n8n 2.42.4 used about 353 MB at idle. Memory rises with the amount of data a workflow processes and the number of workflows running at once.
What else is worth running on the same VPS?
A rank tracker, a metasearch engine and a reporting dashboard all sit comfortably beside n8n. Our guide to the best VPS for SEO tools and automation lists what each tool needs.
Get n8n running, then back it up
The install is short: one Compose file, one Nginx block and a certificate. The parts people skip are the four proxy settings and the backup, and those are the ones that cost time later. Set them up on day one. For n8n alongside other tools we suggest 4 GB of RAM. See the Linux VPS plans and pricing to pick a server.