Self-host guide

How to Self-Host n8n on a VPS

Install n8n on Ubuntu 24.04 with n8n's Docker Compose and Traefik setup, then set the webhook URL, back up the encryption key and update safely.

n8n is a workflow automation tool. You connect apps and APIs as nodes on a visual canvas, and workflows run on triggers, schedules or incoming webhooks. This guide installs it by hand on Ubuntu 24.04 with n8n's own Docker Compose setup, which runs n8n next to Traefik, a reverse proxy that gets a Let's Encrypt certificate and serves n8n over HTTPS.

Prefer one click? Deploy n8n from the VeloxaHost marketplace and skip the manual steps; this guide is for when you want to run every command yourself.

What you need

  • A VeloxaHost server with the Ubuntu 24.04 LTS image. You get root, over SSH or in the browser terminal on the server's page.
  • A domain or subdomain, such as n8n.example.com.
  • A dedicated IPv4 address. Traefik needs ports 80 and 443, and its TLS challenge runs on 443. The shared IPv4 address every server gets has no 80 or 443, only a block of forwarded ports. A dedicated IPv4 is a paid add-on (the console shows the current price): choose it as "Network access" on the create page, or take one from the IP addresses page and attach it. If none is free in the region, the request is refused.

If n8n is only for you and no outside service calls its webhooks, skip the domain and address and use the SSH tunnel variant at the end of the install steps.

n8n's docs give no fixed minimum for a single container. Their Kubernetes example requests 250 MB of memory with a 500 MB limit. The optional n8n Assistant sandbox stack needs at least 4 GB of RAM and 2 vCPUs.

A Veloxa 1 (1 vCPU, 2 GB RAM, 50 GB disk) at $8 a month, billed at $0.02 an hour and capped at the monthly price, fits a small instance. For n8n Assistant or heavy workflows, take a Veloxa 2 (2 vCPU, 4 GB RAM, 80 GB disk) at $15 a month, billed at $0.03 an hour and capped at the monthly price.

Install n8n

  1. The dedicated IPv4 is routed to the server, which must answer on it. Add it to the interface (check the name with ip addr), and to the network configuration so it survives a reboot.

    ip addr add 198.51.100.5/32 dev eth0
    
  2. Create a DNS A record for n8n (or your subdomain) pointing at that address.

  3. Install Docker Engine and the Compose plugin from Docker's apt repository, as in Docker's Ubuntu guide. sudo also works as root.

    # Add Docker's official GPG key:
    sudo apt update
    sudo apt install ca-certificates curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    
    # Add the repository to Apt sources:
    sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
    Types: deb
    URIs: https://download.docker.com/linux/ubuntu
    Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
    Components: stable
    Architectures: $(dpkg --print-architecture)
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF
    
    sudo apt update
    sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    

    Check that it works:

    sudo docker run hello-world
    docker --version
    docker compose version
    
  4. Create a project directory, a local-files directory (shared with n8n as /files for file nodes) and an .env file with your domain, timezone and certificate email.

    mkdir n8n-compose
    cd n8n-compose
    mkdir local-files
    
    # .env
    DOMAIN_NAME=example.com
    SUBDOMAIN=n8n
    GENERIC_TIMEZONE=America/New_York
    SSL_EMAIL=you@example.com
    
  5. Create compose.yaml. This is n8n's file plus N8N_PROXY_HOPS=1, which n8n's docs ask for behind a reverse proxy.

    services:
      traefik:
        image: "traefik"
        restart: always
        command:
          - "--api.insecure=true"
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
          - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
          - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
          - "--entrypoints.websecure.address=:443"
          - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
          - "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
          - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - traefik_data:/letsencrypt
          - /var/run/docker.sock:/var/run/docker.sock:ro
    
      n8n:
        image: n8nio/n8n
        restart: always
        ports:
          - "127.0.0.1:5678:5678"
        labels:
          - traefik.enable=true
          - traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
          - traefik.http.routers.n8n.tls=true
          - traefik.http.routers.n8n.entrypoints=web,websecure
          - traefik.http.routers.n8n.tls.certresolver=mytlschallenge
          - traefik.http.middlewares.n8n.headers.SSLRedirect=true
          - traefik.http.middlewares.n8n.headers.STSSeconds=315360000
          - traefik.http.middlewares.n8n.headers.browserXSSFilter=true
          - traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
          - traefik.http.middlewares.n8n.headers.forceSTSHeader=true
          - traefik.http.middlewares.n8n.headers.SSLHost=${DOMAIN_NAME}
          - traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
          - traefik.http.middlewares.n8n.headers.STSPreload=true
          - traefik.http.routers.n8n.middlewares=n8n@docker
        environment:
          - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
          - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
          - N8N_PORT=5678
          - N8N_PROTOCOL=https
          - NODE_ENV=production
          - N8N_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:
      traefik_data:
    

    N8N_HOST and N8N_PROTOCOL give n8n its public host name and scheme. n8n otherwise builds webhook URLs from protocol, host and port, which behind a proxy would include port 5678, so N8N_WEBHOOK_URL sets the public URL that external services get. It exists from n8n 2.30.0 and replaces WEBHOOK_URL, deprecated from 2.35.0.

  6. Start the stack and check the logs.

    sudo docker compose up -d
    sudo docker compose logs -f n8n
    sudo docker compose logs traefik
    

Private-only variant (SSH tunnel). Without a dedicated IPv4, use this compose.yaml with no Traefik, then tunnel in through the first port of your block, which forwards to SSH.

services:
  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - 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:
ssh -p 20000 -L 5678:127.0.0.1:5678 root@203.0.113.10

Open http://localhost:5678. Schedules and outgoing calls work; webhooks from outside services cannot reach n8n.

First login

Open https://n8n.example.com (or http://localhost:5678 over the tunnel). The signup screen creates the owner account; the password needs eight characters, a number and a capital letter. Do it right after the first start, because whoever completes it first becomes the owner.

SMTP is optional, but without it users cannot reset passwords. Outbound TCP port 25 is blocked, so set N8N_EMAIL_MODE=smtp and N8N_SMTP_PORT to your provider's 587 or 465.

Secure it

In the console, go to Firewalls, New Firewall, Add Rule. Allow TCP 80 and 443 from 0.0.0.0/0 and TCP 22 only from YOUR.IP/32, then attach the firewall on the server's page. Until one is attached, all traffic is allowed.

Ports published by Docker bypass ufw, so rely on the VeloxaHost firewall. The compose file publishes 5678 on 127.0.0.1 only, so outside traffic reaches n8n through Traefik.

From n8n's docs:

  • Keep N8N_SECURE_COOKIE at its default, true.
  • Keep N8N_RESTRICT_FILE_ACCESS_TO=/files to limit file nodes to that directory.
  • Add N8N_PUBLIC_API_DISABLED=true if you do not use the public REST API.
  • N8N_DIAGNOSTICS_ENABLED=false stops telemetry, but also turns off Ask AI in the Code node.
  • Run a security audit now and then:
sudo docker compose exec -u node n8n n8n audit

Back it up

With SQLite, everything is in the n8n_data volume at /home/node/.n8n: the database and the config file holding the credential encryption key (unless you set N8N_ENCRYPTION_KEY yourself). Without the key, restored credentials cannot be decrypted, so store a copy safely.

sudo docker compose exec -u node n8n cat /home/node/.n8n/config

For a full backup, stop n8n so the SQLite file is consistent and archive the volume. Check the container name first.

sudo docker compose stop n8n
sudo docker compose ps --all
sudo docker run --rm --volumes-from n8n-compose-n8n-1 -v $(pwd):/backup ubuntu tar cvf /backup/n8n-data.tar /home/node/.n8n
sudo docker compose up -d

The CLI also exports workflows and credentials as JSON. Write into /files (the host's local-files), since other container paths are lost on recreate. These exports skip users, executions, variables and the key, so they do not replace the volume backup.

sudo docker compose exec -u node n8n n8n export:workflow --backup --output=/files/backups/workflows/
sudo docker compose exec -u node n8n n8n export:credentials --backup --output=/files/backups/credentials/

On VeloxaHost, snapshots are instant and free (5 on Starter, 30 on Pay as you go): server page, Snapshots, name, Create. Restoring overwrites the current state. Automatic snapshots run every hour, 6 hours, 12 hours, daily or weekly, kept for 1, 3, 7, 14 or 30 days. The Backups page creates, downloads and restores server backups (as a new server), on a schedule of every 6 hours, 12 hours, daily or weekly, keeping 1 to 10. Download one now and then for an off-site copy.

Update it

n8n ships a minor version most weeks; update at least monthly and read the release notes for breaking changes. Take a snapshot first, then:

cd n8n-compose
sudo docker compose pull
sudo docker compose down
sudo docker compose up -d

The untagged n8nio/n8n image is the stable release, 2.43.7 when checked. To control timing, pin a tag such as n8nio/n8n:2.43.7.

Troubleshooting

No site or no certificate. Check that the A record resolves to the dedicated IPv4, the address is on the interface and the firewall allows 80 and 443. Then read sudo docker compose logs traefik.

Webhook URLs show localhost or port 5678. Set N8N_WEBHOOK_URL to the full HTTPS URL with a trailing slash and run sudo docker compose up -d.

A secure cookie warning. You opened n8n over plain HTTP. Use the HTTPS domain or the tunnel to localhost rather than setting N8N_SECURE_COOKIE=false.

"Connection Lost" or "JavaScript heap out of memory". A workflow needs more memory. Move to a larger size, or split the work into smaller batches and sub-workflows.

FAQ

What license is n8n released under?

n8n calls itself fair-code and is released under the Sustainable Use License, which is not OSI-approved. It allows use and modification for your own internal business purposes or for non-commercial or personal use. Files marked .ee are under the separate n8n Enterprise License.

Why does n8n need a dedicated IPv4 address on VeloxaHost?

Traefik serves HTTPS on ports 80 and 443 and gets its certificate through a TLS challenge on 443. The shared IPv4 address only offers a block of forwarded ports. A dedicated address also keeps your DNS record valid if the server moves to another host, which can change the shared address and ports.

Do webhooks work over the SSH tunnel?

No. The tunnel only connects your own computer. Outside services need a public HTTPS URL, so use the dedicated IPv4 setup for any workflow started by an incoming webhook.

Where does n8n keep workflows and credentials?

In the n8n_data volume at /home/node/.n8n. With SQLite it holds the database and the config file with the encryption key. Back them up together, because credentials are encrypted with that key.

Which VeloxaHost size fits n8n?

A Veloxa 1 (1 vCPU, 2 GB RAM, 50 GB disk) at $8 a month, billed at $0.02 an hour and capped at the monthly price, suits a small instance. For n8n Assistant (at least 4 GB of RAM and 2 vCPUs) or many parallel executions, choose a Veloxa 2 (2 vCPU, 4 GB RAM, 80 GB disk) at $15 a month, billed at $0.03 an hour and capped at the monthly price.

Commands and settings checked on 2026-10-18 against the official documentation: docs.n8n.io, github.com, docs.docker.com. Software changes; if a step no longer matches, the vendor's docs win.

Ready to deploy?

Start a free trial, or compare plans to match your team's scale.