Mark Lowel Montealto — Full Stack Developer & DevOps Engineer

Mark Lowel
Montealto

Full Stack Developer & DevOps Engineer

Blog

June 2026

·

4 min read

Deploy n8n to Linode with Docker and Nginx

A step-by-step guide to self-hosting n8n on a Linode VPS with Docker, an Nginx reverse proxy, and a free Let's Encrypt TLS certificate.

n8n is an open-source workflow automation tool — think Zapier, but self-hosted, free, and with no node limits. In this guide we'll deploy it on the cheapest Linode plan ($5/mo Nanode 1 GB) using Docker Compose, put it behind Nginx with a free TLS certificate, and make it survive reboots automatically.

Total cost: $5/month.

What we'll build

  • Ubuntu 22.04 VPS on Linode (Nanode 1 GB)
  • Docker + Docker Compose running n8n
  • Nginx as a reverse proxy
  • Free TLS via Let's Encrypt / Certbot
  • Auto-restart on reboot

Prerequisites

  • A Linode account — sign up at linode.com
  • A domain or subdomain you control (e.g. n8n.yourdomain.com)
  • SSH key pair on your local machine (~/.ssh/id_rsa / id_rsa.pub)

Step 1 — Create a Nanode on Linode

The Nanode 1 GB is Linode's cheapest plan at $5/month — 1 vCPU, 1 GB RAM, 25 GB SSD. It's enough for personal automations and small teams.

  1. Log in to cloud.linode.com.
  2. Click Create → Linode.
  3. Choose these options:
SettingValue
ImageUbuntu 22.04 LTS
RegionClosest to you
PlanShared CPU → Nanode 1 GB ($5/mo)
Linode Labeln8n-server
Root PasswordSet a strong password
SSH KeysAdd your public key
  1. Click Create Linode and wait about 60 seconds until the status shows Running.
  2. Copy the IPv4 address from the dashboard — you'll need it in every step.

Point your subdomain at the server now so DNS propagates while you work:

# Add this A record in your DNS provider / registrar
n8n.yourdomain.com   A   YOUR_LINODE_IP

Step 2 — SSH into your server

ssh root@YOUR_LINODE_IP

If you added your SSH key during creation you'll log straight in. Otherwise use the root password you set.


Step 3 — Create a non-root user

Running everything as root is a security risk. Create a deploy user with sudo access.

adduser deploy
usermod -aG sudo deploy

Copy your SSH key to the new user so you can log in as deploy:

rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

Switch to the new user:

su - deploy

Step 4 — Update the system and configure the firewall

sudo apt update && sudo apt upgrade -y

Enable UFW and open only the ports we need:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo ufw status

Expected output:

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere

Step 5 — Install Docker and Docker Compose

# Install Docker via the official convenience script
curl -fsSL https://get.docker.com | sudo sh
 
# Add your user to the docker group (no sudo needed for docker commands)
sudo usermod -aG docker $USER
newgrp docker
 
# Verify both tools are present
docker --version
docker compose version

You should see something like:

Docker version 26.1.3, build b72abbb
Docker Compose version v2.27.1

Step 6 — Set up the n8n directory and environment file

Create a dedicated directory for n8n:

mkdir -p ~/n8n && cd ~/n8n

Create the environment file that Docker Compose will read. This keeps all secrets out of the Compose file itself.

nano ~/n8n/.env

Paste the following — replace every CHANGE_THIS value before saving:

# Domain
N8N_HOST=n8n.yourdomain.com
WEBHOOK_URL=https://n8n.yourdomain.com/
 
# Basic auth — change these!
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=CHANGE_THIS_STRONG_PASSWORD
 
# Encryption key — generate one: openssl rand -hex 32
N8N_ENCRYPTION_KEY=CHANGE_THIS_32_CHAR_HEX_KEY
 
# Timezone
GENERIC_TIMEZONE=Asia/Manila
TZ=Asia/Manila

To generate the encryption key without leaving the terminal:

openssl rand -hex 32

Copy the output and paste it as N8N_ENCRYPTION_KEY in the .env file.


Step 7 — Create the Docker Compose file

nano ~/n8n/docker-compose.yml

Paste this:

services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=${WEBHOOK_URL}
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${TZ}
    volumes:
      - n8n_data:/home/node/.n8n
 
volumes:
  n8n_data:

Note the 127.0.0.1:5678:5678 binding — this exposes n8n only to localhost, not to the open internet. Nginx (Step 8) handles all public traffic over HTTPS.

Start n8n:

cd ~/n8n
docker compose up -d

Check it's running:

docker compose ps
docker compose logs --tail 30

Look for this line in the logs — it means n8n started successfully:

Editor is now accessible via: http://localhost:5678

Step 8 — Install Nginx

sudo apt install -y nginx

Create a server block for n8n:

sudo nano /etc/nginx/sites-available/n8n

Paste the following — replace n8n.yourdomain.com with your actual subdomain:

server {
    listen 80;
    server_name n8n.yourdomain.com;
 
    location / {
        proxy_pass         http://127.0.0.1:5678;
        proxy_http_version 1.1;
 
        # WebSocket support — required for n8n's live editor
        proxy_set_header   Upgrade           $http_upgrade;
        proxy_set_header   Connection        "upgrade";
 
        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 $scheme;
 
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
        client_max_body_size 50m;
    }
}

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

The nginx -t command must print syntax is ok before you reload.


Step 9 — Add a free TLS certificate with Certbot

sudo apt install -y certbot python3-certbot-nginx

Obtain and install the certificate:

sudo certbot --nginx -d n8n.yourdomain.com

Certbot will:

  1. Verify you own the domain (DNS must have propagated from Step 1)
  2. Issue a Let's Encrypt certificate
  3. Automatically rewrite your Nginx config to serve HTTPS
  4. Set up a cron job to renew the cert every 60 days

Test auto-renewal:

sudo certbot renew --dry-run

If the dry run passes, your certificate will renew automatically forever.


Step 10 — Verify the deployment

Open your browser and go to https://n8n.yourdomain.com. You should see the n8n login screen. Log in with the N8N_BASIC_AUTH_USER and N8N_BASIC_AUTH_PASSWORD you set in Step 6.

Test that webhooks work end-to-end:

  1. Click + New Workflow → add a Webhook node
  2. Set the method to GET and click Listen for test event
  3. Copy the test URL and run:
curl -u admin:YOUR_PASSWORD "https://n8n.yourdomain.com/webhook-test/YOUR_PATH"

You should see Workflow was started in the n8n editor.


Step 11 — Enable auto-restart on reboot

Docker's restart: unless-stopped policy (set in the Compose file) handles this automatically. Verify Docker itself starts on boot:

sudo systemctl enable docker
sudo systemctl status docker

To confirm, reboot and check:

sudo reboot
# Wait ~30 seconds then SSH back in
ssh deploy@YOUR_LINODE_IP
docker ps

n8n should be listed as Up X seconds.


Upgrading n8n

Pull the latest image and recreate the container. Your data (workflows, credentials) are stored in the n8n_data Docker volume and survive upgrades.

cd ~/n8n
docker compose pull
docker compose up -d

Troubleshooting

502 Bad Gateway

n8n hasn't started yet or crashed. Check:

docker compose logs --tail 50

Common cause: wrong N8N_ENCRYPTION_KEY format — it must be a hex string, not a plaintext passphrase. Regenerate with openssl rand -hex 32.

Certbot fails: "Could not resolve host"

DNS hasn't propagated yet. Check propagation with:

dig n8n.yourdomain.com +short

It should return your Linode IP. Wait 5–30 minutes and retry.

Webhook calls time out from external services

Confirm WEBHOOK_URL in .env ends with a trailing slash:

WEBHOOK_URL=https://n8n.yourdomain.com/

Then recreate the container:

cd ~/n8n && docker compose up -d --force-recreate

Forgot the basic-auth password

Edit ~/n8n/.env, change N8N_BASIC_AUTH_PASSWORD, then:

cd ~/n8n && docker compose up -d --force-recreate

You now have a production-ready n8n instance running on the cheapest Linode plan for $5/month — fully encrypted, auto-restarting, and with persistent storage so no data is lost on upgrades or reboots.