n8n: Connect Everything With Visual Workflow Automation
You have got a collection of services running on your Pi. Docker containers doing their thing. Uptime Kuma watching them. Ollama summarising text. Pi-hole filtering DNS. Each one works. None of them talk to each other.
That is the difference between having containers and having infrastructure. Infrastructure is connected. When something breaks, something else notices and does something about it. When data arrives, it flows through a pipeline without you lifting a finger. When a backup completes, you get confirmation. When it fails, you get woken up.
n8n is the thing that connects all of it. It is self-hosted Zapier, except it runs on your hardware, your data never leaves your network, and it has actual power — not the watered-down “500 tasks per month” nonsense that cloud automation platforms gate behind enterprise pricing.
Career Impact: The gap between a sysadmin and a DevOps engineer is almost always automation. Not “I wrote a bash script once” automation — systematic, event-driven, self-healing automation that connects monitoring to remediation to notification. That is what n8n teaches you. Enterprise automation platforms (ServiceNow, Azure Logic Apps, AWS Step Functions) all follow the same patterns: triggers, conditions, actions, error handling. Learn them once on n8n and you can walk into any iPaaS conversation with confidence.
What n8n Actually Is
n8n (pronounced “nodemation”) is a workflow automation platform. You build automations visually by connecting nodes on a canvas — a trigger starts the workflow, data flows through processing nodes, and actions happen at the end. No code required for most things, but you can drop into JavaScript when you need to.
Think of it this way: if you have ever built a pipeline in your head (“when Uptime Kuma detects a failure, restart the container, then send me a Telegram message”), n8n lets you actually build that pipeline and have it run automatically.
It has over 400 built-in integrations — Telegram, email, RSS, HTTP, databases, Ollama, Docker, and hundreds more. But the real power is webhooks. Any service that can send an HTTP request can trigger an n8n workflow, which means anything in your homelab can be connected to anything else.
Why self-hosted matters here: Cloud automation platforms like Zapier and Make bill by task or operation, so the more your automations run, the higher the plan you need. Self-hosted n8n Community Edition has no execution limits and no licence fee. On a Pi that costs 3p a day in electricity.
Installation on Your Pi 5
n8n runs beautifully in Docker. If you have followed the series from the beginning, you already have Docker and Docker Compose ready to go.
Create a directory for n8n:
mkdir -p ~/services/n8n
cd ~/services/n8n
Create your docker-compose.yml:
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=YOUR_PI_IP # the address you browse to; not a bind address
- N8N_PORT=5678
- N8N_PROTOCOL=http
- N8N_SECURE_COOKIE=false # needed while you use plain http on your LAN
- WEBHOOK_URL=http://YOUR_PI_IP:5678/
- GENERIC_TIMEZONE=Europe/London
- TZ=Europe/London
- N8N_METRICS=true
volumes:
- n8n_data:/home/node/.n8n
networks:
- n8n-network
networks:
n8n-network:
driver: bridge
volumes:
n8n_data:
There are no login credentials in this file, and that is deliberate. The N8N_BASIC_AUTH_* variables that older guides use were removed in n8n 1.0: n8n now has its own user management, and you create the owner account in the browser on first launch. This compose file is for n8n 2.x. N8N_SECURE_COOKIE=false is needed because n8n otherwise refuses logins over plain HTTP from anywhere but localhost; remove it once n8n is behind HTTPS. From n8n 2.35, WEBHOOK_URL is also accepted under its new name N8N_WEBHOOK_URL; the old name still works but logs a deprecation warning.
Start it up:
docker compose up -d
Give it 30 seconds (n8n is not the fastest to initialise on ARM), then open http://your-pi-ip:5678 in your browser. You will be prompted to create an owner account on first launch.
Memory Warning: n8n is not lightweight. It runs on Node.js and will happily consume 300-500MB of RAM at idle, more under load. On an 8GB Pi 5 running several other services, you need to keep an eye on this. If you are also running Ollama, you may need to choose between having both running simultaneously or starting Ollama only when you need it. Run docker stats to see what is using what.
Pro Tip: Set WEBHOOK_URL to the actual URL that external services will use to reach n8n. If you have set up Nginx Proxy Manager from the earlier guide, use your proxied URL (e.g., https://n8n.yourdomain.com/). If webhooks use the wrong URL, they will silently fail and you will spend an hour wondering why nothing triggers.
The Visual Workflow Editor
When you first open n8n, you are looking at the workflow canvas. It is a blank space where you drag, drop, and connect nodes. If you have ever used Visio or draw.io, the concept is the same — except these boxes actually do things.
Every workflow starts with a trigger. Something that says “start running now.” That could be:
- Schedule Trigger — run every hour, daily at 9am, every Monday
- Webhook — run when an HTTP request hits a specific URL
- Email Trigger — run when an email arrives
- Manual — run when you click the button (for testing)
After the trigger, you chain action nodes. Each one does something with the data: transform it, send it somewhere, make a decision, call an API. Data flows left to right through the chain.
Let us build your first workflow to get the feel of it.
Your First Workflow: Daily System Report
- Create a new workflow
- Click the + button to add your first node
- Search for Schedule Trigger and add it
- Configure it to run daily at 08:00
- Click + on the trigger’s output to add the next node
- Search for SSH and choose the Execute Command operation. This runs a shell command on your Pi over SSH, using the SSH credential described in the security note further down. (Not the Execute Command node: in Docker it runs inside the n8n container, which has no
dockercommand, and n8n 2.x disables it by default.) - Enter:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | head -20 - Add another node: Send Email (or Telegram, which I find far easier for homelab alerts)
- Configure it to send the output to yourself
Click Execute Workflow to test it. You should see data flow through each node, and an email or message arrive with your container status.
That is the core loop: trigger, process, act. Everything else builds on this.
Real Workflow 1: Self-Healing Infrastructure
This is the workflow that made me take n8n seriously. Instead of getting a “service down” alert and manually SSHing in to restart things, the automation handles it.
The Flow: Uptime Kuma → Restart Container → Verify → Alert
Uptime Kuma (from the monitoring guide earlier in this series) can send webhooks when a service goes down. We catch that webhook in n8n, attempt a restart, check if it worked, and notify you either way.
Step 1: Set up the webhook trigger in n8n
Add a Webhook node. Set the method to POST and note the webhook URL it generates (something like http://your-pi:5678/webhook/uptime-restart). You will need this for Uptime Kuma.
Step 2: Parse the alert
Add a Set node to extract the container name from the webhook payload. Uptime Kuma sends JSON with the monitor name and status. Map the monitor name to your actual Docker container name:
// In a Set (Edit Fields) node, extract what we need
containerName: {{ $json.body.monitor.name.toLowerCase().replace(/ /g, '-') }}
status: {{ $json.body.heartbeat.status }}
message: {{ $json.body.msg }}
The Webhook node puts the request body under body, which is why the expressions start with $json.body.
Step 3: Add a condition
Add an IF node. Only proceed if the status indicates “down” (status = 0 in Uptime Kuma). You do not want to restart containers when the “up” notification fires.
Step 4: Restart the container
On the “true” branch, add an SSH node (Execute Command operation, using the SSH credential to the Docker host described in the security note below) with this command:
docker restart {{ $json.containerName }}
Step 5: Wait and verify
Add a Wait node (30 seconds), then another SSH node. This prints the container ID only if the container is running, and nothing if it is not. (Avoid docker inspect --format '{{.State.Running}}' here: n8n would try to read the Go template braces as an expression.)
docker ps -q --filter "name=^{{ $('Edit Fields').item.json.containerName }}$" --filter status=running
Step 6: Notify
Add another IF node checking whether the SSH node’s stdout is not empty, meaning the container is now running. On success, send a Telegram message: “Container X was down, auto-restarted, now healthy.” On failure: “Container X is down, auto-restart failed, manual intervention required.”
Step 7: Configure Uptime Kuma
In Uptime Kuma, go to your monitor’s notification settings and add a webhook notification pointing to your n8n webhook URL.
This entire flow takes about 15 minutes to build. Once it is running, your Pi fixes itself at 3am while you sleep. That is not a homelab trick — that is exactly how enterprise auto-remediation works.
Security Note: Why SSH rather than the Execute Command node? When n8n runs in Docker, Execute Command runs inside the n8n container, not on the Pi. The official image has no docker command (and no apk to install one without building a custom image), so mounting the Docker socket alone does not make docker restart work. n8n 2.0 also disables Execute Command by default (re-enabling it means setting NODES_EXCLUDE=[]). The SSH node avoids all of that: create an SSH credential in n8n for the Pi itself (its LAN IP, a user that is in the docker group, and a private key you generated for n8n alone), and the commands run on the host. Be aware that a user in the docker group is effectively root on that host, so use a dedicated key, keep n8n off the public internet, and in production use a more restricted approach.
Real Workflow 2: AI-Powered News Digest
This workflow pulls RSS feeds, sends the articles through Ollama for summarisation, and delivers a daily digest to your inbox or Telegram. It combines three things from this series: n8n (automation), Ollama (local AI), and self-hosted infrastructure (no data leaves your network).
The Flow: Schedule → RSS Feeds → Filter → Ollama → Digest → Deliver
Step 1: Schedule trigger
Daily at 07:00. You want your digest ready before you start your day.
Step 2: RSS Feed nodes
Add multiple RSS Read nodes, one per feed. Point them at whatever you follow — tech news, security advisories, industry blogs. Set each one to fetch the last 24 hours of articles.
Feed URLs (examples):
- https://feeds.arstechnica.com/arstechnica/index
- https://www.theregister.com/headlines.atom
- https://feeds.feedburner.com/TheHackersNews
Step 3: Merge and deduplicate
Add a Merge node to combine all feeds into a single list. Then use a Remove Duplicates node keyed on the article URL.
Step 4: Limit the batch
Add a Limit node. Ollama on a Pi 5 is not fast — summarising 50 articles will take a very long time. Limit to 10-15 articles maximum. You can increase this later if you find performance acceptable.
Step 5: Summarise with Ollama
Add a Basic LLM Chain node with an Ollama Chat Model sub-node attached. In the Ollama credential, set the base URL to your Ollama instance (http://your-pi-ip:11434; localhost will not work when n8n runs in a container) and use a prompt like:
Summarise this article in 2-3 sentences. Focus on why it matters
for IT infrastructure professionals. Be concise and factual.
Title: {{ $json.title }}
Content: {{ $json.contentSnippet || $json.content }}
Step 6: Build a single message
Add a Code node (mode: Run Once for All Items) to format everything into one readable digest. The LLM Chain returns only the summary text, so the code pairs each summary with its article from the Limit node by position:
const articles = $('Limit').all();
const summaries = $input.all();
let digest = `📰 Daily Tech Digest - ${new Date().toLocaleDateString('en-GB')}\n\n`;
summaries.forEach((item, i) => {
const a = articles[i].json;
digest += `${i + 1}. ${a.title}\n`;
digest += `${item.json.text}\n`;
digest += `${a.link}\n\n`;
});
digest += `---\n${summaries.length} articles processed via Ollama`;
return [{ json: { digest } }];
Step 7: Deliver via Telegram
Add a Telegram node and send the digest to yourself. You now have a private, AI-summarised news briefing that runs entirely on your own hardware.
I run a more complex version of this on my own infrastructure. It pulls from about 20 sources, filters by relevance, and produces a security-focused intelligence briefing. The pattern is identical — the only difference is more feeds and a more detailed prompt.
Performance Reality: Ollama on a Pi 5 with 8GB running a 3B parameter model takes roughly 5-15 seconds per article summary. A 15-article digest will take 2-4 minutes to process. That is perfectly fine for a daily batch job that runs at 7am. It is not fine for real-time processing. Plan your workflows around this constraint.
Real Workflow 3: Automated Backups With Validation
Backups that you never verify are not backups. They are hopes. This workflow runs your backup, checks that it actually produced output, validates the file size is reasonable, and alerts you to the result.
The Flow: Schedule → Run Backup → Validate → Alert
Step 1: Schedule trigger
Daily at 02:00. Backups should run when the Pi is least busy.
Step 2: Execute the backup
An SSH node (Execute Command operation, using the SSH credential to the Docker host described in the security note below) that runs your backup script on the Pi. For example, backing up Docker volumes (adjust the names to match docker volume ls, since Compose prefixes volume names with the project name):
#!/bin/bash
BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# Stop containers, backup volumes, restart.
# n8n is deliberately not in this list: stopping n8n from inside its own
# workflow kills the run. Back n8n up from cron instead, or export the
# workflows as JSON (see Backup Your Workflows below).
for service in gitea uptime-kuma; do
docker stop "$service" 2>/dev/null
docker run --rm -v "${service}_data:/data" -v "$BACKUP_DIR:/backup" \
alpine tar czf "/backup/${service}.tar.gz" /data
docker start "$service"
done
# Output results for n8n to process
ls -la "$BACKUP_DIR"
Step 3: Validate the backup
Another SSH node to check files exist and are not suspiciously small:
BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
FAILED=""
for file in gitea.tar.gz uptime-kuma.tar.gz; do
filepath="$BACKUP_DIR/$file"
if [ ! -f "$filepath" ]; then
FAILED="$FAILED $file:MISSING"
elif [ $(stat -f%z "$filepath" 2>/dev/null || stat -c%s "$filepath") -lt 1024 ]; then
FAILED="$FAILED $file:TOO_SMALL"
fi
done
if [ -z "$FAILED" ]; then
echo "OK"
else
echo "FAILED:$FAILED"
fi
Step 4: Branch on result
An IF node on the SSH node’s stdout. If the output starts with “OK,” send a green Telegram message. If it contains “FAILED,” send a red alert with details of what went wrong.
Step 5: Cleanup old backups
Add a final SSH node that removes backups older than 7 days:
find /backups -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +
When this workflow fires, you wake up to a Telegram message saying either “Backups completed successfully” or “Backup FAILED: gitea.tar.gz missing.” You know the state of your backups every single day without thinking about it.
Webhooks and Trigger Architecture
Webhooks are the most powerful trigger type in n8n, and understanding them is genuinely valuable for any integration role.
A webhook is simply an HTTP endpoint that n8n creates and listens on. When something sends a POST request to that URL, the workflow fires. The data in the request body becomes available to subsequent nodes.
Common Webhook Patterns
- Uptime Kuma → n8n: Monitor goes down, sends JSON payload with monitor details
- Gitea → n8n: Code pushed to repository, triggers deployment pipeline
- Grafana → n8n: Alert fires, sends alert details for processing
- Home Assistant → n8n: Automation triggers, sends event data
- Any application → n8n: Custom webhook for anything with HTTP capability
Webhook Security
By default, n8n webhook URLs are unprotected. Anyone who knows the URL can trigger your workflow. For a homelab on a private network, this is usually acceptable. If your n8n instance is exposed to the internet (through a reverse proxy), add authentication:
# In the Webhook node settings:
Authentication: Header Auth
Header Name: X-Webhook-Secret
Header Value: your-secret-token-here
Then configure the sending service to include that header. This is basic but effective — it prevents random internet traffic from triggering your workflows.
Beyond Webhooks: Other Trigger Types
Schedule triggers run on a cron-like schedule. Daily reports, weekly cleanup, hourly checks.
Polling triggers check an external source periodically. RSS feeds, email inboxes, API endpoints. n8n handles the polling interval and deduplication.
Event triggers for specific integrations. The Telegram trigger listens for messages to a bot. The email trigger watches an inbox. These are essentially managed webhooks that n8n sets up for you.
Connecting to External APIs
Not everything has a native n8n node. For services without built-in integration, the HTTP Request node is your universal connector.
Any API that accepts HTTP requests works. You configure the method (GET, POST, PUT, DELETE), the URL, headers, authentication, and body. The response becomes available to the next node.
Example: Querying Your Pi-hole Stats
Pi-hole v6 (the old /admin/api.php endpoint was removed in v6)
HTTP Request node 1 (log in):
Method: POST
URL: http://your-pihole-ip/api/auth
Body (JSON): {"password": "your-app-password"}
Returns a session ID in session.sid
HTTP Request node 2 (stats):
Method: GET
URL: http://your-pihole-ip/api/stats/summary
Header: X-FTL-SID = {{ $json.session.sid }}
Response includes:
- queries.total
- queries.blocked
- queries.percent_blocked
- gravity.domains_being_blocked
Use a Pi-hole application password (generated in the Pi-hole web interface settings) rather than your main password, and store it as an n8n credential rather than typing it into the node.
Chain this with a schedule trigger and a Telegram node, and you have a daily Pi-hole stats report. Simple, but it demonstrates the pattern: trigger → API call → process response → deliver.
Authentication Patterns
n8n supports credential storage for API keys, OAuth tokens, and basic auth. Store credentials once, reference them in multiple workflows. Never hardcode tokens in your workflow JSON — n8n encrypts stored credentials, but workflow exports include everything else in plaintext.
This is the same principle you will encounter in enterprise platforms. ServiceNow, Azure Logic Apps, AWS Step Functions — they all have credential vaults. Learning the habit of separating secrets from logic starts here.
Pro Tip: Always prefer n8n’s native nodes over the HTTP Request node. The Telegram node handles message formatting, error handling, and rate limiting. The HTTP Request node to the Telegram API does not. Native nodes exist for a reason — they handle the edge cases you have not thought of yet.
Error Handling That Actually Works
Workflows fail. APIs timeout. Containers crash. The question is not whether your automation will fail — it is whether it fails gracefully or silently.
The Error Workflow Pattern
n8n has a built-in error workflow feature. Create a separate workflow that receives error data and does something with it (send a Telegram message, log to a file, create an incident). Then set every workflow to use this error handler.
Error Workflow Setup:
1. Create workflow: "Error Handler"
2. Trigger: Error Trigger node
3. Action: Telegram node with error details
4. Template: "⚠️ Workflow '{{ $json.workflow.name }}' failed
Error: {{ $json.execution.error.message }}
Failed at: {{ $json.execution.lastNodeExecuted }}
Execution: {{ $json.execution.url }}"
5. In every other workflow: Settings → Error Workflow → select "Error Handler"
Now every workflow failure produces a notification. No more silent failures where an automation broke three weeks ago and you only noticed when the thing it was supposed to do had not happened.
Retry Logic
For HTTP requests to external APIs, configure retries in the node settings. An API timeout does not always mean failure — sometimes the service was just slow. Turning on Retry On Fail with 2-3 tries and a wait between tries handles most transient failures.
For critical workflows, add explicit retry logic with an IF node: if the first attempt fails, wait 60 seconds, try again. If the second attempt fails, escalate to an alert instead of retrying forever.
Gotchas, Pain Points, and Honest Advice
Memory Is the Bottleneck
n8n running on Node.js on a Pi 5 is not a lightweight combination. Expect 300-500MB at idle, spiking higher during complex workflows. If you are running n8n alongside Ollama, Pi-hole, Uptime Kuma, and Nginx Proxy Manager, you will need to watch your memory carefully.
Run docker stats regularly. If you start seeing OOM kills, either reduce the number of concurrent services or add swap space (which is slow on a Pi but better than crashing).
Workflow Complexity Creep
It is extremely tempting to build one massive workflow that does everything. Resist this. A 50-node workflow is nearly impossible to debug when something breaks in the middle. Break complex automations into smaller workflows that trigger each other via webhooks. Each workflow should do one thing well.
The Docker Access Security Trade-off
Giving n8n an SSH key for a user in the docker group (or mounting the Docker socket into any container) gives it full control over Docker: it can stop, start, and delete any container, which amounts to root on that host. On a single-user homelab, this is practical and useful. In any shared or production environment, it is a significant security risk. Be aware of what you are granting access to.
Time Zone Confusion
Set GENERIC_TIMEZONE and TZ in your environment variables. If you do not, scheduled workflows will fire at UTC times, not local times. I spent an embarrassingly long time wondering why my “8am daily report” was arriving at 8am UTC (which is not 8am in the UK for half the year).
Webhook URLs Change
Recreating the container keeps your webhook paths as long as the data volume survives, but if you move n8n to a different host, change its hostname or port, or change WEBHOOK_URL, the URLs external services must call change with it. Any external service pointing at the old URL will silently fail. After any migration, check every webhook configuration in your external services. Write them down somewhere (like your Gitea repository, which we will cover in the next guide).
Backup Your Workflows
n8n stores workflows in a SQLite database inside the Docker volume. If that volume disappears, so do all your workflows. Export important workflows as JSON regularly, and store those exports in Git. n8n’s interface lets you download any workflow as a JSON file — make this part of your backup routine.
ARM Compatibility: Most n8n community nodes work on ARM (Pi 5), but not all. If you install a community node and n8n crashes on restart, that node likely has a native dependency compiled for x86 only. Check the node’s GitHub issues for ARM compatibility before installing. The core n8n nodes all work perfectly on ARM.
Putting It All Together
Here is what a well-connected homelab looks like once n8n is wired in:
- Monitoring: Uptime Kuma detects a container crash → n8n restarts it → verifies recovery → sends Telegram confirmation
- Intelligence: RSS feeds collected daily → filtered by relevance → summarised by Ollama → formatted digest delivered to Telegram
- Backups: Nightly backup script → validation → old backup cleanup → status notification
- Deployment: Code pushed to Gitea → webhook fires → n8n pulls latest code → rebuilds container → runs health check
- Reporting: Daily system health check → Docker stats, disk usage, Pi temperature → morning briefing message
None of these require your intervention. They run 24/7 on a Raspberry Pi that draws a few watts. When something needs your attention, it tells you. When it does not, it stays quiet.
That is not a hobby project. That is operations.
Career Translation
The skills you build with n8n translate directly to enterprise roles:
| n8n Skill | Enterprise Equivalent | Roles |
|---|---|---|
| Visual workflow design | Azure Logic Apps, AWS Step Functions, ServiceNow | Automation Engineer |
| Webhook architectures | Event-driven microservices, API gateways | Integration Architect |
| Error handling patterns | Circuit breakers, dead letter queues, retry policies | Platform Engineer, SRE |
| API integration | Enterprise iPaaS (MuleSoft, Boomi, Workato) | Integration Specialist |
| Self-healing automation | Auto-remediation, runbook automation | SRE, DevOps Engineer |
| Scheduled pipelines | Data pipelines, ETL processes, batch processing | Data Engineer |
When an interview question asks “describe an automation you have built,” you do not talk about a tutorial you followed. You talk about a self-healing monitoring pipeline that restarts failed services, validates the recovery, and sends you a report. You talk about an AI-powered news aggregation system running on hardware you manage. You talk about automated backups with validation and alerting.
Those are not theoretical skills. They are operational experience. The same patterns at enterprise scale use different tools but identical logic. Trigger, condition, action, error handling, notification. The platform changes; the thinking does not.
What to Do Next
- Build the daily report workflow first. It is simple, immediately useful, and teaches the core concepts.
- Add the self-healing workflow if you have Uptime Kuma from the monitoring guide. This is the one that will impress in interviews.
- Connect Ollama if you set it up in the AI guide. Even a basic summarisation pipeline demonstrates AI integration skills.
- Export your workflows as JSON and commit them to version control — which is exactly what we cover in the next and final guide.
Next in the series: Gitea: Own Your Code, Track Your Infrastructure — version control for everything you have built so far, including those n8n workflow exports.
Previous: K3s: Real Kubernetes on a Raspberry Pi
Series hub: Raspberry Pi 5 Homelab: 10 Projects That Actually Advance Your Career
RTFM Apparel · printed in the UK

ReadTheManual is run, written and curated by Eric Lonsdale.
Eric has over 20 years of professional experience in IT infrastructure, cloud architecture, and cybersecurity, but started with PCs long before that.
He built his first machine from parts bought off tables at the local college campus, hoping they worked. He learned on BBC Micros and Atari units in the early 90s, and has built almost every PC he’s used between 1995 and now.
From helpdesk to infrastructure architect, Eric has worked across enterprise datacentres, Azure environments, and security operations. He’s managed teams, trained engineers, and spent two decades solving the problems this site teaches you to solve.
ReadTheManual exists because Eric believes the best way to learn IT is to build things, break things, and actually read the manual. Every guide on this site runs on infrastructure he owns and maintains.
Enjoyed this guide?
New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.







