Self-hosted n8n

How to Securely Expose a Self-Hosted n8n Instance with Nginx on Ubuntu VPS
Running n8n on your own VPS gives you full control over data and integrations, but exposing it securely takes a bit of care. In this guide I’ll walk you through setting up n8n behind Nginx on an Ubuntu 22 VPS, using docker-compose and a dedicated subdomain (n8n.your-domain.xyz
).
We’ll focus on the parts that actually matter: container config, reverse proxy, TLS certificates, and Google OAuth2 readiness.
Prerequisites
- Ubuntu 22 VPS with public IP
- Domain with a subdomain pointed to the VPS (
A
record forn8n.your-domain.xyz
) - Docker + docker-compose installed
- Nginx installed (
sudo apt install nginx
) - UFW/iptables open on ports 80 and 443
Step 1: docker-compose for n8n
Bind n8n to localhost so it isn’t directly exposed, and tell it about the external host/protocol:
# docker-compose.yml
services:
n8n:
image: n8nio/n8n:latest
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678" # only accessible through Nginx
environment:
- N8N_HOST=n8n.your-domain.xyz
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.your-domain.xyz
- N8N_EDITOR_BASE_URL=https://n8n.your-domain.xyz
- N8N_ENCRYPTION_KEY=replace-with-random-string
- N8N_TRUSTED_PROXIES=127.0.0.1
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=replace-with-strong-password
volumes:
- ./n8n_data:/home/node/.n8n
Bring it up:
docker compose up -d
Hint:N8N_TRUSTED_PROXIES
is important. Without it you’ll see Express warnings aboutX-Forwarded-For
headers, and rate limiting won’t work properly.
Step 2: Minimal Nginx reverse proxy
Create /etc/nginx/sites-available/n8n.conf
:
server {
listen 80;
server_name n8n.your-domain.xyz;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name n8n.your-domain.xyz;
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://127.0.0.1:5678/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
client_max_body_size 50m;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Required if you stick with WebSockets (n8n editor uses them by default)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable it and test:
sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Step 3: TLS with certbot
Install certbot with the Nginx plugin:
sudo apt install -y certbot python3-certbot-nginx
Obtain the cert:
sudo certbot --nginx -d n8n.your-domain.xyz
Certbot Nginx plugin should automatically update Nginx configuration with the ssl certificate locations
ssl_certificate /etc/letsencrypt/live/n8n.your-domain.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.your-domain.xyz/privkey.pem;
Check auto-renew:
sudo certbot renew --dry-run
Tip: Make sure your DNS resolves and port 80 is open before running certbot. If you have an AAAA record but no IPv6 open, Let’s Encrypt will fail. Either configure IPv6 in Nginx or remove the AAAA record.
Step 4: WebSockets
By default the n8n editor uses WebSockets for live updates. If you see browser console errors like:
[WebSocketClient] Connection error
then your proxy is missing the Upgrade
headers. Add the lines shown in the Nginx config above.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Step 5: Google OAuth2 redirect for Gmail
For the Gmail integration it's worth to configure the Google Credentials. Setting up in Google Console is not covered here.
When you create OAuth credentials in Google Cloud Console, the Authorized redirect URI must match:
https://n8n.your-domain.xyz/rest/oauth2-credential/callback
With the proxy and HTTPS working, Gmail OAuth handshakes succeed and tokens refresh without keeping any tunnel open.
Extra Security Tips
- Leave N8N_BASIC_AUTH enabled for a first login wall.
- Add auth_basic in Nginx if you want double-layer protection.
- Backup your
n8n_data
volume and keep yourN8N_ENCRYPTION_KEY
safe.
Consider rate limiting:
limit_req_zone $binary_remote_addr zone=n8n:10m rate=5r/s;
Wrap-up
That’s the essentials: a docker-compose service bound to localhost, Nginx reverse proxy with HTTPS, trusted proxy headers, and correct OAuth redirect URI. With this setup your self-hosted n8n instance at https://n8n.your-domain.xyz
is secure, ready for Gmail integration, and future-proof.