I've been running n8n self-hosted on my VPS for over a year. What started as a simple webhook handler evolved into the backbone of my entire ops automation stack — disk space monitoring, service uptime alerts, Docker cleanup, and backup verification all running for €0 in software cost. If you're still SSHing in manually to check `df -h` or restarting services by hand, this tutorial will fix that permanently.
Most guides on this topic use n8n to call a hosting provider's API — meaning you need a separate server for n8n and a specific cloud's API credentials. This tutorial takes the simpler, more powerful approach: n8n runs directly on the VPS you're managing. It gets direct OS access via Execute Command nodes, works on any Linux VPS (Hetzner, Vultr, Hostinger, Contabo — anything), and costs nothing extra.
Prerequisites
- A Linux VPS running Ubuntu 22.04+ — minimum 1 GB RAM (I use Hostinger KVM 1 and Hetzner CX22 for side projects; either works)
- Docker and Docker Compose installed on the server
- A Telegram account — you'll create a free bot in Step 2 for instant alerts
- Basic Linux terminal comfort — you'll run 3–4 commands total
Need a VPS? For vps for self-hosted applications in 2026, Hetzner CX22 is the best starting point — €5.99/mo for 2 vCPU, 4 GB RAM, and 20 TB monthly bandwidth.
Hetzner Cloud — Best Value · 4.9/5
Best price-per-resource for self-hosted workloads. CX22 at €5.99/mo — 2 vCPU, 4 GB RAM, 20 TB bandwidth. The go-to VPS for running n8n and side-project infrastructure.
What We're Building
One complete, production-ready workflow: a daily disk space monitor that checks your server's storage, and fires a Telegram alert the moment usage crosses 80%. You'll run through every node — Cron, Execute Command, Code, IF, and Telegram — so you understand the pattern. Once you do, adding more workflows takes 15 minutes each.
At the end, I'll show you the 3 workflows I actually run in production: uptime watchdog, Docker cleanup, and backup verification.
Step 1: Install n8n Self-Hosted with Docker
SSH into your VPS and create a working directory:
mkdir ~/n8n && cd ~/n8n
Create a docker-compose.yml file:
services:
n8n:
image: n8nio/n8n
restart: always
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=changethis
volumes:
- ~/.n8n:/home/node/.n8n
Start it:
docker compose up -d
Open http://your-server-ip:5678 in your browser. n8n's UI loads immediately. For production, put Nginx or Caddy in front with SSL — but for this tutorial, direct access works fine.
This is the open-source workflow automation self-hosted approach — no monthly subscription, no execution limits, no external dependency.
n8n — Most Flexible · 4.7/5
Open-source workflow automation — self-host for free with unlimited workflows and executions. Cloud plan from €24/mo if you'd rather skip the server setup.
Step 2: Create a Telegram Bot for Alerts
Telegram's free bot API is the fastest way to wire up operational alerts. You'll have it working in under 5 minutes.
- Open Telegram and search for @BotFather
- Send
/newbotand follow the prompts to name your bot - Copy the API token BotFather returns — it looks like
7123456789:ABCDEFabcdef_xyz - Start a conversation with your new bot (search for its username, click Start)
- Visit
https://api.telegram.org/bot<YOUR_TOKEN>/getUpdatesin your browser - Find the
"id"value inside the"chat"object — that's your Chat ID
Save both the token and chat ID — you'll paste them into n8n in Step 5.
Step 3: Build the Disk Monitoring Workflow
In the n8n UI, click + New Workflow. You'll build 5 nodes connected in sequence.
Node 1 — Schedule Trigger: Search for Schedule Trigger. Set it to run Every Day at 8:00 AM. This fires the workflow every morning before your workday starts.
Node 2 — Execute Command: Add an Execute Command node. In the Command field, enter:
df / | awk 'NR==2 {print $5}' | tr -d '%'
This runs directly on the VPS and returns a single integer — the root partition's used percentage (e.g., 73). No API calls, no credentials, no network dependency.
Node 3 — Code: Add a Code node (JavaScript mode). Paste this:
const raw = $input.first().json.stdout.trim();
const diskPercent = parseInt(raw, 10);
if (isNaN(diskPercent)) {
throw new Error('Failed to parse disk usage: ' + raw);
}
return [{ json: { diskPercent, threshold: 80, breached: diskPercent >= 80 } }];
This parses the raw output and adds a breached flag that the IF node reads next.
Step 4: Add the IF Condition and Telegram Alert
Node 4 — IF: Add an IF node. Set the condition: Value 1 = {{ $json.diskPercent }}, Operation = Greater or Equal, Value 2 = 80.
The true branch fires when disk is at or above 80%. Wire a Telegram node to it. The false branch goes nowhere — no alert when everything is healthy.
Node 5 — Telegram (true branch): Add a Telegram node. Create a new credential with your bot token. Set the Chat ID to the one from Step 2. Use this message template:
⚠️ *Disk Alert* — {{ $('Code').item.json.diskPercent }}% used
Threshold: {{ $('Code').item.json.threshold }}%
Checked: {{ $now.toISO() }}
Action: ssh user@your-vps-ip and run: du -sh /* | sort -rh | head -10
The last line tells you exactly what to run when you get the alert. Operational alerts should be actionable, not just alarming.
Step 5: Test and Activate the Workflow
Click Execute Workflow (the play button at the top). n8n runs all 5 nodes and shows the output at each step.
If disk is below 80%, the IF node routes to the false branch — nothing fires. To test the Telegram message path without filling up your disk: temporarily change the IF threshold to 10, run once, then set it back to 80.
Once tested, click Activate (top-right toggle). The workflow will now run every morning at 8:00 AM automatically.
Tips & Common Mistakes
- Execute Command vs SSH node: Execute Command only works when n8n runs on the same server you're monitoring. If n8n is on a separate machine, use the SSH node with your server's credentials instead — same commands, different node.
- Telegram chat ID gotcha: You must send at least one message to your bot before
getUpdatesreturns your chat ID. If the API response is empty, message the bot first, then refresh. - n8n runs as non-root by default: The Docker image runs as the
nodeuser. Commands likedfandfreework fine without root. For commands that need elevated access, configure a minimalsudoersentry — don't run the n8n container as root. - Store credentials properly: Use n8n's built-in Credentials system for your Telegram token. Never hardcode tokens directly in node parameters — credentials are encrypted at rest, parameters are not.
- Reverse proxy for production: Once you're done testing, put n8n behind Nginx or Caddy with an SSL cert. The
certbot --nginxcommand handles Let's Encrypt in about 3 minutes.
Next Steps: 3 More Workflows I Run in Production
With the disk monitor pattern clear, these three n8n workflows for AI and general VPS ops follow the same structure and take about 15 minutes each to set up.
1. HTTP Service Uptime Watchdog
Schedule Trigger (every 5 minutes) → HTTP Request node (GET your service URL, e.g., http://localhost:3000/health) → IF Status Code not equal to 200 → Telegram alert + optional Execute Command to restart the service (docker restart container-name). This replaces a basic external uptime monitor and keeps you notified within 5 minutes of any service going down.
2. Weekly Docker Image Cleanup
Schedule Trigger (Sundays at 2:00 AM) → Execute Command: docker system prune -f 2>&1 → Telegram confirmation with the bytes reclaimed. I recovered 4+ GB in the first run on a 6-month-old server. Runs silently every week, sends one line of confirmation.
3. Backup Verification Alert
Schedule Trigger (daily, 10 minutes after your backup cron) → Execute Command: ls -lt /path/to/backups | head -5 → Code node to check if the newest file is from today → IF older than 24 hours → Telegram alert. This catches the silent backup failures that only surface during a restore. I've had this trigger twice in the past year — both times the backup script had silently broken.
All three patterns are identical to what you built: trigger → check → condition → notify. Once n8n is running on your VPS, the only limit is what bash commands can tell you — which is everything.