Skip to content 99% OFF 🎉 Anniversary Sale 99% OFF Shared Hosting Use Code HURRYUP Claim Offer 99% OFF Hosting
99% OFF Hosting — Code HURRYUP
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting cPanel Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Call +91 75795 45488
Login
Hosting Panel — cPanel & Billing Console Panel — VPS Management
ALL SYSTEMS OPERATIONAL
VPS

Install n8n on a VPS: Self-Hosted Automation Setup Guide

Getwebup 6 min read

If you've been paying for Zapier or Make and watching the task-based billing climb every month, you've probably looked at n8n. It's open-source, the workflow logic runs on hardware you control, and once it's installed there's no per-execution fee. The catch is that "self-hosted" means you're the one who has to install it, put HTTPS in front of it, and keep it backed up. Here's how to do that properly on a Getwebup VPS, plus the handful of mistakes that trip people up in their first week.

What You'll Need

  • A VPS running Ubuntu 22.04 or 24.04 — 1 vCPU / 2GB RAM is enough to start; bump to 4GB if you'll run heavier workflows with a lot of parallel executions.
  • A domain or subdomain pointed at the VPS (e.g. automate.yourdomain.com). Webhooks and OAuth logins won't work reliably without HTTPS, and you can't get a certificate without DNS pointed correctly first.
  • SSH access with a non-root sudo user.
  • 15–20 minutes.

Step 1: Install Docker and Docker Compose

n8n's own docs recommend Docker for production, and it's genuinely the least painful route — you avoid wrestling with Node versions and system-wide npm packages on a VPS you'll want to upgrade later.

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
docker --version
docker compose version

If docker compose version doesn't return anything, your Docker install is old enough that Compose isn't bundled — install the plugin separately with sudo apt install docker-compose-plugin.

Step 2: Set Up a Real Database (Skip SQLite)

n8n defaults to SQLite if you don't configure anything, and that's fine for a five-minute test. For anything you'll actually rely on, use Postgres instead — SQLite on n8n has a known history of database-locked errors once you have more than a couple of workflows running concurrently, and there's no clean way to inspect or repair it under load.

Create a working directory and a .env file:

mkdir -p ~/n8n-stack && cd ~/n8n-stack
nano .env
DOMAIN_NAME=automate.yourdomain.com
GENERIC_TIMEZONE=Asia/Kolkata
POSTGRES_USER=n8n
POSTGRES_PASSWORD=use-a-long-random-string-here
POSTGRES_DB=n8n
N8N_ENCRYPTION_KEY=generate-a-32-char-random-string-and-save-it-offline

That last line matters more than it looks. Save it somewhere outside the server too — more on that in the Fixes section.

Step 3: The Docker Compose File

This is a minimal but production-sane stack: Postgres for data, n8n itself, and no exposed database port to the outside world.

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

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=${DOMAIN_NAME}
      - N8N_PROTOCOL=https
      - N8N_PORT=5678
      - WEBHOOK_URL=https://${DOMAIN_NAME}/
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_USER=${POSTGRES_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - postgres

volumes:
  postgres_data:
  n8n_data:

Notice n8n is bound to 127.0.0.1:5678, not 0.0.0.0. That's intentional — nothing reaches port 5678 from the internet directly. Nginx handles the public-facing side.

docker compose up -d
docker compose ps

Step 4: Nginx Reverse Proxy and HTTPS

Install Nginx and Certbot, then point a server block at the container:

sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx
server {
    listen 80;
    server_name automate.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        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;
    }
}

The Upgrade/Connection headers aren't optional — n8n's editor uses a websocket connection for live workflow updates, and without them the UI loads but feels half-broken (nodes won't update, execution status hangs).

sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d automate.yourdomain.com

Visit https://automate.yourdomain.com and you'll land on the owner account setup screen — that's your first and only chance to set the admin email and password through the UI, so don't skip it and leave the instance open.

Common Errors and Fixes

SymptomCauseFix
Webhooks never fire, or the "Test URL" shows an internal Docker hostnameWEBHOOK_URL or N8N_HOST missing or still pointing at an IPSet both to your real HTTPS domain in .env, then docker compose up -d to recreate the container
502 Bad Gateway from NginxContainer isn't running, or Nginx is proxying to the wrong portRun docker compose ps; if it's up, confirm the proxy_pass port matches the one in your compose file
"Credentials could not be decrypted" after a restore or moveThe server got a new N8N_ENCRYPTION_KEY instead of the original oneRestore the exact original key — there's no recovery path, credentials must be re-entered from scratch
Disk fills up over a few weeksEvery past execution's data (input/output of each node) is kept indefinitely by defaultSet EXECUTIONS_DATA_PRUNE=true and EXECUTIONS_DATA_MAX_AGE=336 (hours) in your environment
WebSocket disconnects, editor feels laggyMissing Upgrade/Connection headers on the Nginx proxy blockAdd the two headers shown in Step 4, then reload Nginx

Backups: What Actually Needs Saving

Three things, not just "the server":

  • The Postgres database — workflows, credentials (encrypted), and execution history. docker compose exec postgres pg_dump -U n8n n8n > n8n-backup.sql, cron it nightly, and copy it off the VPS.
  • N8N_ENCRYPTION_KEY — without this exact value, the credentials in your database backup are unreadable junk. Keep a copy in a password manager, not just in .env on the same disk you're backing up.
  • The n8n_data volume — holds binary data for certain node types and some local settings. docker run --rm -v n8n-stack_n8n_data:/data -v $(pwd):/backup alpine tar czf /backup/n8n-data.tar.gz -C /data .

Prevention: Keep It Healthy

  • Never publish port 5678 with 0.0.0.0 in the compose file — always go through Nginx over HTTPS.
  • Turn on n8n's basic-auth or SSO options if the instance runs anything customer-facing, on top of the owner login.
  • Set the execution pruning variables from day one — cleaning up six months of accumulated execution logs later is a much bigger job than preventing it.
  • Before updating, pull the new image and check n8n's release notes for breaking changes to node behavior — some major versions have changed how credentials or expressions resolve.
  • docker compose pull && docker compose up -d is the whole update process once you've confirmed the release notes are clean.

Frequently asked questions

Can I just use n8n's default SQLite database on a VPS?

You can, and it works fine for testing a handful of workflows. But SQLite has a documented history of 'database is locked' errors once multiple workflows execute at the same time, and there's no good way to repair it under load. Switch to Postgres before you put anything business-critical on it.

Do I really need a domain, or can I use the VPS's IP address?

You need a domain with HTTPS. Webhook nodes and most OAuth-based credentials (Google, Slack, etc.) either refuse plain HTTP or behave unreliably over it. Point an A record at your VPS first, then run Certbot as shown in Step 4.

I lost my N8N_ENCRYPTION_KEY — can support recover my credentials?

No. n8n encrypts stored credentials with that key, and there's no backdoor or reset process — it's by design. If the key is gone, every credential in the instance has to be re-entered manually. This is the single most common self-inflicted incident with self-hosted n8n, which is why it's worth saving in a password manager the day you install it.

How much VPS should I budget for n8n?

1 vCPU and 2GB RAM handles light-to-moderate use — a few dozen workflows firing occasionally. If you're running heavy AI-agent workflows, large file processing, or many workflows in parallel, move up to 4GB RAM and consider a separate VPS for Postgres if the database starts competing with n8n for memory.

Does n8n need a queue mode / separate worker on a single VPS?

Not for most setups. Queue mode with Redis and separate worker processes is meant for high-volume production loads spread across multiple servers. A single VPS running n8n in its default 'main' mode is the right starting point — you can migrate to queue mode later without losing your workflows.

#n8n #vps #self-hosted #docker #workflow-automation #nginx

Keep reading

Chat with Support