itsez.dev
๐Ÿ“–Tutorial

How to install n8n on a VPS

2026-07-30ยท4 min readยทAPIs & automation

n8n connects APIs and services through visual workflows that you can operate on your own infrastructure. This guide runs n8n with PostgreSQL and Docker Compose, keeps the editor private behind HTTPS and covers the configuration needed for reliable webhooks.

Prerequisites

Use a clean Ubuntu 22.04 or 24.04 VPS with SSH, a public IP and at least 2 GB of RAM. Add memory when workflows execute code, process files or run concurrently. Create an A record such as n8n.example.com pointing to the VPS and reserve ports 80 and 443 for the reverse proxy.

You need Docker Engine, the Compose plugin, a backup destination and a generated encryption key. Do not paste API tokens into the Compose file. Store them in n8n credentials or a secret manager.

Step 1, Connecting to your server

Update the server and create a private application directory:

ssh root@SERVER_IP
apt update && apt upgrade -y
apt install -y ca-certificates curl
mkdir -p /opt/n8n
cd /opt/n8n
chmod 750 /opt/n8n

Check that ports 80 and 443 are free. If an old web server is already running, stop it only after identifying what depends on it. The n8n editor should not be exposed directly on a public port.

Step 2, Installing Docker and Docker Compose

Install Docker from its official Ubuntu instructions, then verify the Compose plugin:

docker --version
docker compose version
systemctl enable --now docker

Use a supported Docker package source and keep the engine updated. Do not add the Docker socket to an unrelated public-facing container.

Step 3, Running n8n with Docker Compose

Create .env with placeholders replaced by unique random values. The encryption key must remain stable for the lifetime of the installation:

POSTGRES_DB=n8n
POSTGRES_USER=n8n
POSTGRES_PASSWORD=REPLACE_WITH_A_RANDOM_VALUE
N8N_ENCRYPTION_KEY=REPLACE_WITH_A_LONG_RANDOM_VALUE
N8N_HOST=n8n.example.com
N8N_PROTOCOL=https
N8N_WEBHOOK_URL=https://n8n.example.com/
N8N_EDITOR_BASE_URL=https://n8n.example.com/
N8N_PROXY_HOPS=1

Create compose.yaml:

services:
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data

  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    restart: unless-stopped
    depends_on:
      - db
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: db
      DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
      DB_POSTGRESDB_USER: ${POSTGRES_USER}
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      N8N_HOST: ${N8N_HOST}
      N8N_PROTOCOL: ${N8N_PROTOCOL}
      N8N_WEBHOOK_URL: ${N8N_WEBHOOK_URL}
      N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL}
      N8N_PROXY_HOPS: 1
    ports:
      - "127.0.0.1:5678:5678"
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  db_data:
  n8n_data:

Start the project and inspect logs:

docker compose up -d
docker compose ps
docker compose logs --tail 100 n8n

Pin image versions after testing rather than relying on latest for production updates.

Step 4, Configuring the reverse proxy

Configure Caddy, Nginx or your platform proxy to forward n8n.example.com to 127.0.0.1:5678. The proxy must include these three headers for webhooks to function correctly:

  • X-Forwarded-For โ€” preserves the original client IP
  • X-Forwarded-Host โ€” preserves the original hostname
  • X-Forwarded-Proto โ€” preserves the original protocol (HTTP/HTTPS)

Let the proxy handle HTTPS and keep 5678 bound to localhost and blocked by the firewall.

Test both the editor and a webhook endpoint. If a provider cannot reach the webhook, check DNS, certificate validity, proxy headers and the N8N_WEBHOOK_URL value before changing n8n workflow code.

Step 5, First access and initial setup

Open https://n8n.example.com and create the owner account. n8n enforces this since v0.200+, so the first user to register becomes the instance owner. Enable user management and limit invitations. Create one test credential, run a simple workflow and verify that its execution data is stored after a restart.

Review execution retention and disable verbose data storage where it is not needed. Credentials should be created inside n8n, not hard-coded in nodes or environment files that are copied around.

Maintenance

Back up the n8n_data volume (which contains the encryption key), PostgreSQL and the encryption key together. If the n8n_data volume is lost, all stored credentials become unrecoverable. A database backup without the encryption key cannot decrypt stored credentials. Upgrade one version at a time when the release notes require it, test workflows after each upgrade and keep a rollback copy.

Monitor queue time, memory, disk and failed executions. For larger workloads, move to the queue mode documented by n8n and provision a separate worker architecture instead of increasing a single container indefinitely.

Tools mentioned

n8n

โ†—

Open-source workflow automation platform to connect apps and build automations visually.

FreemiumNo cardOSS

Free self-hosted community edition with unlimited workflows. Cloud free tier with 5 active workflows.

ABOUT US

Honest, independent, no fluff.

No paid placements. Just a clear look at what this does, what it costs, and what to know before you commit.

Read moreโ†’

FAQ

Questions, answered.

Should n8n use SQLite or PostgreSQL in production?๏ผ‹

Use PostgreSQL for a long-lived or collaborative installation. SQLite is useful for a quick evaluation but does not remove the need for backups.

Why do n8n webhooks need a public URL?๏ผ‹

External services must reach the webhook endpoint. Set the public HTTPS URL and forward requests through the reverse proxy.

Where does n8n store credentials?๏ผ‹

Credentials are encrypted with the configured encryption key. Keep that key outside source control and back it up with the database.