Self-hosted n8n

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 for n8n.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 about X-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 your N8N_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.