How to Self-Host n8n on a VPS for Production Use
Running n8n on your own VPS is the single best way to get unmetered workflow automation with full data control. But there is a meaningful difference between “n8n running on a server” and “n8n running in production.” A quick test install with SQLite and no TLS will work — right up until the container restarts and your executions vanish, or a webhook fails because your endpoint isn’t reachable over HTTPS.
This self host n8n VPS setup guide walks you through a production-grade deployment: Docker on Linux, PostgreSQL for persistent data, HTTPS via a reverse proxy, and the operational practices that keep workflows reliable month after month. Treat it as your n8n production deployment guide — every step is aimed at a setup you can actually depend on in 2026.
Prerequisites for Self-Hosting n8n
Before touching the terminal, make sure you have the following in place:
- A VPS or dedicated server running a modern Linux distribution (Ubuntu 22.04/24.04 LTS or Debian 12 are safe choices). For most teams, 2 vCPUs and 4 GB RAM is a comfortable starting point; scale up if you plan to run heavy or highly concurrent workflows.
- Root or sudo access to the server.
- A domain or subdomain (e.g.,
n8n.yourcompany.com) with an A record pointed at your server’s IP. Webhooks and OAuth integrations effectively require a proper HTTPS URL. - Basic firewall hygiene: allow ports 22 (SSH), 80, and 443; block everything else by default (e.g., with
ufw). - Docker and Docker Compose installed. On Ubuntu/Debian:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Log out and back in for the group change to take effect, then verify with docker --version and docker compose version.
If you are still deciding whether n8n is the right platform for your team, you may want to compare self-hosted automation tools before committing to a production build-out.
How Do I Set Up n8n on a VPS with Docker?
Docker is the recommended deployment method for self-hosted n8n. It isolates dependencies, makes upgrades trivial, and keeps your configuration declarative in a single Compose file.
n8n Docker Setup for Linux
[IMAGE: Docker command line interface for n8n linux setup]
Step 1 — Create a project directory:
mkdir -p /opt/n8n && cd /opt/n8n
Step 2 — Create a .env file for your secrets and configuration. Never hard-code these into the Compose file:
# /opt/n8n/.env
POSTGRES_USER=n8n
POSTGRES_PASSWORD=<generate-a-strong-password>
POSTGRES_DB=n8n
N8N_ENCRYPTION_KEY=<generate-a-long-random-string>
N8N_HOST=n8n.yourcompany.com
WEBHOOK_URL=https://n8n.yourcompany.com/
GENERIC_TIMEZONE=America/New_York
The N8N_ENCRYPTION_KEY encrypts stored credentials. Back this key up somewhere safe — if you lose it, saved credentials cannot be decrypted, even with a full database backup.
Step 3 — Create docker-compose.yml:
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
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- 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}
- WEBHOOK_URL=${WEBHOOK_URL}
- N8N_PROTOCOL=https
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:
Note that n8n’s port is bound to 127.0.0.1 — it should never be exposed directly to the internet. A reverse proxy will handle public traffic.
Step 4 — Start the stack:
docker compose up -d
docker compose logs -f n8n
Step 5 — Put a reverse proxy in front. Install Nginx (or Caddy/Traefik) and obtain a TLS certificate with Let’s Encrypt:
sudo apt install nginx certbot python3-certbot-nginx
sudo certbot --nginx -d n8n.yourcompany.com
A minimal Nginx server block proxies https://n8n.yourcompany.com to http://127.0.0.1:5678, with WebSocket upgrade headers enabled (n8n’s editor uses live connections). Once TLS is active, browse to your domain, create the owner account, and your instance is live.
How to Set Up Persistent Postgres Data for n8n
By default, a bare n8n install uses SQLite. That is fine for experiments, but a production n8n postgres setup with persistent data is strongly recommended for durability, concurrent access, and easy backups.
[IMAGE: Postgres database configuration for n8n persistent data]
The Compose file above already implements the two critical persistence layers:
- A named Docker volume for Postgres (
postgres_data) — this stores workflows, credentials (encrypted), execution history, and user accounts. Because it is a named volume, the data survives container restarts, upgrades, and rebuilds. - A named volume for n8n itself (
n8n_data) — this holds instance-level configuration and, importantly, the fallback encryption key file.
Beyond the volumes, follow these persistence best practices:
- Test the restart path immediately. Run
docker compose down && docker compose up -dand confirm your workflows and credentials are still there. Discover persistence problems on day one, not after six months of building. - Schedule database backups. A nightly
pg_dumpshipped off-server is the minimum:
docker exec n8n-postgres-1 pg_dump -U n8n n8n | gzip > /backups/n8n-$(date +%F).sql.gz
Pair the dump with a copy of your .env file (which contains the encryption key). A database backup without the encryption key cannot restore working credentials.
– Prune execution history. Execution logs grow indefinitely by default. Set EXECUTIONS_DATA_PRUNE=true and EXECUTIONS_DATA_MAX_AGE (in hours) to keep the database lean.
– Restrict Postgres to the internal Docker network. In the Compose file above, Postgres exposes no host port at all — only n8n can reach it.
How to Ensure n8n Reliability and Uptime in Production
Deployment is half the job; operations is the other half. These practices address n8n reliability and uptime in production:
1. Use restart policies. The restart: unless-stopped directive ensures both containers come back automatically after crashes or server reboots. Verify Docker itself is enabled as a system service: sudo systemctl enable docker.
2. Monitor the health endpoint. n8n exposes /healthz for liveness checks. Point an external uptime monitor at it so you learn about outages before your users do — an internal monitor cannot tell you the server itself is down.
3. Handle workflow-level failures. Reliability isn’t just process uptime. Inside n8n:
- Attach an Error Workflow to production workflows so failures trigger alerts (email, Slack, or a ticket).
- Configure retry on fail for nodes calling flaky external APIs.
- Use idempotent workflow design where possible, so re-runs after a failure don’t duplicate side effects.
4. Upgrade deliberately. Pin awareness to n8n’s release notes, back up the database before upgrading, then:
docker compose pull && docker compose up -d
Avoid latest-chasing on a schedule you don’t control; upgrade when you have read the changelog and have a fresh backup.
5. Plan for scale. A single instance handles substantial load, but if you grow into high-throughput territory, n8n supports queue mode with Redis and separate worker containers, letting you scale execution horizontally while keeping one editor instance.
6. Watch server resources. Set up basic monitoring for disk, RAM, and CPU. The most common “n8n went down” root cause in self-hosted setups is a full disk from unpruned executions or logs.
Next Steps: Building Your First Workflow
With a hardened instance running, the fun part begins. Two natural starting points for ops teams:
- Event-driven workflows. Learn to configure webhook triggers in n8n so external systems — forms, monitoring tools, calendars — can kick off automations in real time.
- Internal operations. Build n8n IT ticketing automation that routes alerts to Slack and creates tickets automatically, turning your new instance into an ops force multiplier from week one.
Your self-hosted instance now gives you what no SaaS automation plan can: unmetered executions, full data residency, and an automation platform that answers only to you.
FAQ
How do I set up n8n on a VPS with Docker?
Install Docker and Docker Compose on a Linux VPS, create a Compose file defining an n8n container and a PostgreSQL container with named volumes, bind n8n to localhost, and place an Nginx (or Caddy) reverse proxy with a Let’s Encrypt TLS certificate in front of it. The full step-by-step configuration is provided above.
How do I set up persistent Postgres data for n8n?
Set DB_TYPE=postgresdb and the related DB_POSTGRESDB_* environment variables, and mount a named Docker volume at /var/lib/postgresql/data in the Postgres container. Verify persistence by restarting the stack, and schedule nightly pg_dump backups alongside a copy of your N8N_ENCRYPTION_KEY.
How do I ensure n8n reliability and uptime in production?
Combine infrastructure measures (restart policies, external monitoring of the /healthz endpoint, disk/RAM monitoring, deliberate upgrades with pre-upgrade backups) with workflow-level measures (error workflows, node retries, idempotent design). For high throughput, run n8n in queue mode with Redis-backed workers.
What are the minimum server requirements for self-hosting n8n?
A small instance runs on modest hardware — 2 vCPUs and 4 GB RAM is a comfortable baseline for a team’s internal automations. Requirements grow with execution volume, payload sizes, and concurrency, so monitor resource usage and scale the VPS as your workflow library grows.
Do I need a domain name to self-host n8n?
Strictly speaking, no — but in practice, yes. Webhook-based integrations and OAuth credential flows require a stable, publicly reachable HTTPS URL. A subdomain with a free Let’s Encrypt certificate is the standard approach.