Subscription fatigue is real. Six months ago I was paying for both Zapier Professional ($89/month) and Make.com Pro ($159/month). The combined bill sat at $248/mo. As our AI agent workflows and webhook volume kept climbing, the projection for the next quarter was already past $350.

I got tired of watching the invoices grow. So I moved everything.

This is the exact process I used to migrate 42 production workflows to a self-hosted n8n instance on a cheap Hetzner VPS, put it behind Cloudflare Zero Trust, and cut the monthly cost to $7.70. Net savings: $240.30 every month ($2,883.60 annually, a 96.8% TCO cut). And yes — zero webhook payloads were lost in the process.

TL;DR FinOps Cost Breakdown: Before vs After

Expense Item Old Commercial SaaS Stack Self-Hosted n8n Stack
Zapier Professional (Tier 2) $89.00 / month $0.00 (Decommissioned)
Make.com Pro (Tier 3) $159.00 / month $0.00 (Decommissioned)
VPS Hosting (Hetzner CX22 2 vCPU / 4GB RAM) $0.00 $6.20 / month
Cloudflare Tunnel & Backups (S3/R2) $0.00 $1.50 / month
TOTAL MONTHLY TCO $248.00 / month $7.70 / month (96.8% Savings)

Net Monthly Capital Saved: $240.30 / month ($2,883.60 / year).

The Migration: What Actually Happened

I didn’t do a big-bang cutover. That would have been stupid.

Step 1: Production Server Provisioning

I spun up a Hetzner CX22 (2 vCPU / 4 GB RAM) running Ubuntu 24.04 LTS. Docker + Docker Compose, nothing fancy. The important part: I never exposed port 5678 to the public internet. Everything went through a Cloudflare Zero Trust Tunnel with automatic TLS.

Here’s the exact compose file I still use (with cloudflared running alongside n8n in the same internal Docker network so no public ports need to be exposed):

# docker-compose.prod.yml
version: '3.8'

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:latest
    container_name: n8n-production
    restart: always
    environment:
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - N8N_HOST=automations.yourdomain.com
      - WEBHOOK_URL=https://automations.yourdomain.com/
      - GENERIC_TIMEZONE=Asia/Jakarta
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=336 # 14 days retention
    volumes:
      - ./n8n_storage:/home/node/.n8n
    networks:
      - internal-net

  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared-tunnel
    restart: always
    command: tunnel run
    environment:
      - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN} # Stored securely in .env
    networks:
      - internal-net

networks:
  internal-net:
    driver: bridge

Took about 25 minutes from empty VPS to first successful webhook test. The rest of the time was spent double-checking the Tunnel routes so I wouldn’t lock myself out.

Step 2: Workflow Porting Strategy

I refused to move all 42 workflows at once. That way lies madness and 3 a.m. Slack alerts.

I split them into three risk tiers and moved them over three nights:

  • Tier 1 (Low Risk – Internal Crons): Daily RSS aggregators, Slack reminder bots, newsletter digests. These were the easy wins. If something broke, only I would notice.
  • Tier 2 (Medium Risk – Asynchronous Webhooks): Form parsers, Stripe receipt forwarders, CRM lead scoring. These had external side effects, so I watched them carefully for 48 hours after each migration.
  • Tier 3 (High Risk – Financial & Auth Critical): User onboarding sequences, AI agent webhook callbacks, database syncs. These waited until the last night. I kept the original Zapier/Make versions running in parallel for 24 hours as a safety net.

The only real pain point: one Stripe webhook that relied on a very specific header order. Cloudflare Tunnel was stripping something. Took me almost two hours of comparing raw request dumps before I found it. Classic.

Lessons Learned & Operational Gotchas

The money part was instant. The operational part is where you actually earn the savings.

  • SQLite is fine… until it isn’t: Default SQLite handled everything without drama up to roughly 10,000 executions per week. Beyond that the write locks start becoming noticeable. I switched to a small Postgres container the moment I saw the first slow query. Don’t wait until it hurts.
  • Backups are not optional: I wrote a tiny daily cron that tars the n8n_storage directory, encrypts it with AES-256, and pushes it to Cloudflare R2 (S3-compatible). Takes literally a few lines of bash. I sleep better knowing I can rebuild the entire instance in under 15 minutes if the VPS disappears:
#!/usr/bin/env bash
# /opt/scripts/backup-n8n.sh — Automated encrypted offsite backup
set -euo pipefail

BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/tmp/n8n_backups"
mkdir -p "${BACKUP_DIR}"

# 1. Tar storage directory & encrypt on the fly with AES-256
tar -czf - -C /opt/n8n n8n_storage | \
  openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
  -out "${BACKUP_DIR}/n8n_${BACKUP_DATE}.tar.gz.enc" \
  -k "${BACKUP_ENCRYPTION_KEY}"

# 2. Push encrypted payload to offsite S3 / Cloudflare R2 bucket
aws s3 cp "${BACKUP_DIR}/n8n_${BACKUP_DATE}.tar.gz.enc" \
  "s3://my-n8n-backups/backups/n8n_${BACKUP_DATE}.tar.gz.enc" \
  --endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com"

# 3. Clean up local temp files
rm -rf "${BACKUP_DIR}"
  • Self-hosting is not “set and forget”: You trade money for a small amount of vigilance. Image updates, disk monitoring, and the occasional Tunnel reconnect are now part of the routine. Still massively cheaper than $248/month.

Frequently Asked Questions

1. Is self-hosted n8n limited in features compared to n8n Cloud?

No. The community edition gives you unlimited workflows, unlimited executions, and all 500+ nodes. The only things locked behind the enterprise license are SSO and some advanced audit/log features that most solopreneurs and small teams never need.

2. What happens if the VPS crashes?

Docker is set to restart: always. Combined with the daily encrypted volume backup, a full reboot brings the instance back in under 30 seconds. I’ve tested it. It works.

That’s the real story. No dramatic "I reinvented automation" narrative. Just a practical move that stopped the bleeding and gave me back control (and $240 every month). If you’re staring at a similar bill, the path is clearer than most people make it look.