Tidqom — Source-linked AI and developer tools
AITid
AI

Self-Hosting n8n on a $6 VPS: Full Setup, Backups Included

Docker compose file, HTTPS, backups, and the memory limit that stopped my instance dying every three days. Running cost: about six dollars a month.

Jordan Mills profile photo
July 29, 2026 · 4 min read
Self-Hosting n8n on a $6 VPS: Full Setup, Backups Included — AI

What this gets you

your own automation server, unlimited workflow executions, your data on your machine, for the price of a coffee per month. Setup takes about 25 minutes if you have a domain ready.

I moved off a hosted plan when my execution count made the monthly bill silly. Eight months in, total cost is the VPS and nothing else.

What you need

Related: Midjourney vs DALL-E vs Flux: A Practical Image-Tool Comparison Framework →

Related: كيف تؤتمت عملك بالذكاء الاصطناعي باستخدام n8n مجاناً (دليل عملي خطوة بخطوة 2026) →

  • A VPS with 2GB RAM (1GB works but see the memory section — it will hurt)
  • A domain, with an A record pointing at the server IP
  • Docker and Docker Compose installed
bash
curl -fsSL https://get.docker.com | sh

The compose file

Related: 9 Free AI Coding Tools Every Developer Should Try in 2026 →

Related: 8 AI Workflow Automation Tools That Actually Save Hours a Week →

yaml
services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: n8n
    volumes: [ "./pgdata:/var/lib/postgresql/data" ]

n8n: image: docker.n8n.io/n8nio/n8n restart: unless-stopped depends_on: [ db ] environment: DB_TYPE: postgresdb DB_POSTGRESDB_HOST: db DB_POSTGRESDB_USER: n8n DB_POSTGRESDB_PASSWORD: ${DB_PASSWORD} N8N_HOST: ${DOMAIN} WEBHOOK_URL: https://${DOMAIN}/ N8N_PROTOCOL: https GENERIC_TIMEZONE: Europe/London N8N_ENCRYPTION_KEY: ${ENCRYPTION_KEY} EXECUTIONS_DATA_PRUNE: "true" EXECUTIONS_DATA_MAX_AGE: "336" NODE_OPTIONS: "--max-old-space-size=768" volumes: [ "./n8n:/home/node/.n8n" ]

caddy: image: caddy:2-alpine restart: unless-stopped ports: [ "80:80", "443:443" ] volumes: - ./Caddyfile:/etc/caddy/Caddyfile - ./caddy:/data

terminal
Advertisement — In Article

Caddyfile:

terminal
{$DOMAIN} {
    reverse_proxy n8n:5678
}

.env:

terminal
DOMAIN=automation.example.com
DB_PASSWORD=<long random string>
ENCRYPTION_KEY=<32+ random chars, never change this later>

Then:

bash
docker compose up -d
docker compose logs -f n8n

Caddy fetches a certificate automatically. Open the domain, create the owner account, done.

Experience log: the first week's problems

Related: Sora 2 Review: OpenAI's Video Model Is Finally Useful for Real Work →

Related: MCP Servers, Explained by Someone Who Wired Five of Them Up →

It died every two or three days with no clear error. dmesg showed the OOM killer. Node's default heap grows until the box runs out. The NODE_OPTIONS: --max-old-space-size=768 line above is the fix on a 2GB box — set it to roughly 40% of total RAM. Zero crashes since.

The database grew to 4GB in three weeks. Every execution stores its full payload. EXECUTIONS_DATA_PRUNE with a 336-hour (14-day) age cap keeps it flat. If you handle large files, also set EXECUTIONS_DATA_SAVE_ON_SUCCESS: none — you rarely need the payload of a run that worked.

Webhooks returned 404 from the outside. WEBHOOK_URL was unset, so n8n generated localhost URLs and registered them with external services. Setting it correctly and re-saving each affected workflow fixed it. Re-saving matters: the URL is stored when the workflow is activated.

Advertisement — In Article

I lost credentials during a rebuild. Credentials are encrypted with N8N_ENCRYPTION_KEY. I regenerated the key on a fresh deploy and every stored credential became unreadable. Back that key up somewhere separate from the server, today.

Backups

Related: How to Clone Your Voice with AI in 2026 (Free and Paid Options) →

Related: Best Zero-Cost AI Tools for Automated Crypto Scalping in 2026 (Tested) →

One cron job:

bash
#!/bin/bash
cd /opt/n8n
docker compose exec -T db pg_dump -U n8n n8n | gzip > /backups/n8n-$(date +%F).sql.gz
tar czf /backups/n8nfiles-$(date +%F).tar.gz ./n8n
find /backups -mtime +14 -delete
terminal
0 3 * * * /opt/n8n/backup.sh

Then copy /backups off the machine — a backup that lives only on the server being backed up is not a backup. I push mine to object storage with rclone.

Restore test, which you should do once before you need it:

bash
gunzip -c n8n-2026-05-01.sql.gz | docker compose exec -T db psql -U n8n n8n

Hardening in ten minutes

Related: How AI Agents Are Quietly Rewiring Enterprise Workflows in 2026 →

  • Disable password SSH, key auth only.
  • ufw allow 22,80,443/tcp and deny everything else — never expose 5678 directly.
  • Turn on two-factor in n8n settings.
  • Set N8N_BLOCK_ENV_ACCESS_IN_NODE: "true" so workflow code cannot read your server environment.

Is self-hosting worth it?

Related: Twin: The 'AI Company Builder' Betting on No-Code Agent Automation →

Break-even in my case was around 3,000 executions a month. Below that, hosted is cheaper once you value the maintenance time honestly — roughly an hour a month for updates and the occasional restart. Above it, self-hosting is dramatically cheaper and you also get unrestricted access to community nodes.

FAQ

How do I update? docker compose pull && docker compose up -d. Take a database dump first; read the release notes for breaking changes on major versions.

Can I run it without a domain? Yes over plain HTTP on an IP, but webhooks from most services require HTTPS. A domain is worth the few dollars a year.

Is 1GB RAM viable? For a handful of simple workflows, yes with a 512MB heap cap. Anything with large payloads or parallel executions will thrash.

What it actually cost me, month by month

ItemMonthly costNote
VPS, 2 vCPU / 2GB RAM$6ran 14 active workflows comfortably
Domain + TLS~$1Let's Encrypt is free, domain amortised
Object storage for backups$1weekly database dumps
Model API calls$3-9varies; filtering first cut this by ~70%
Total$11-17the equivalent hosted plan quoted me far more

Setup log: the four things that bit me

  • Webhooks returned 404 behind the reverse proxy. Cause: WEBHOOK_URL still pointed at localhost. Set it to the public HTTPS URL before testing anything.
  • Workflows vanished after a container rebuild. No volume mounted for the database. Mount it before you build your first workflow, not after.
  • Disk filled in six weeks. Execution logs. EXECUTIONS_DATA_MAX_AGE=336 (14 days) fixed it permanently.
  • A silent failure ran unnoticed for three days. An expired OAuth token. Every workflow now has an error branch that pings me on failure — treat it as mandatory, not optional.

New to n8n entirely? Start with the full step-by-step build: automating your work with n8n.

Advertisement

Related Articles

مقالات ذات صلة — تابع القراءة داخل الموقع

View all in AI

مواضيع مقترحة · Suggested Topics

استكشف مواضيع ومحاور ذات صلة بهذا المقال — روابط داخلية لتعميق قراءتك.

The Daily Pulse

Newsletter delivery is not connected yet. This form only saves your address in this browser; no email is sent.

Get concise, source-linked technology notes without the hype.

Advertisement