The Home Lab
My photos, passwords, and automation workflows were spread across a dozen SaaS tools and cloud providers (me trying to maximally utilise different free-tiers :P) - each with its own subscription, limits, and general overhead of managing. I replaced all of them with a single self-hosted system running on a repurposed MacBook Air.
Here are the details on how I built a low-cost, production-grade infrastructure that scratches most of my experimentation itch these days.
How it all connects
All traffic from the internet hits Cloudflare first - TLS, caching, DDoS protection, the works.
A lightweight cloudflared daemon on the MacBook maintains an outbound-only tunnel, so nothing
on my home network is directly reachable from outside. A second laptop with an NVIDIA GPU sits on the
local network purely for Immich's ML workloads (face detection, smart search).
Key tradeoff: I went with Cloudflare Tunnels over port forwarding or a self-managed reverse proxy. Since it eliminates the attack surface entirely and easy to manage for a simple home setup. Zero open ports, no router config to maintain, and has been worth it.
┌──────────────────────── NETWORK TOPOLOGY ─────────────────────────┐ │ │ │ INTERNET │ │ │ │ │ ▼ │ │ ┌──────────────────────────┐ │ │ │ Cloudflare Edge │ │ │ │ DNS + TLS + WAF │ │ │ └────────────┬─────────────┘ │ │ │ outbound tunnel only │ │ ▼ │ │ ┌───────────────────────────┐ ┌───────────────────┐ │ │ │ UBUNTU-MAC │ │ WINDOWS-GPU │ │ │ │ Primary Server │◄►│ ML Offload Node│ │ │ │ │ML│ │ │ │ │ ├─ immich:2283 [●] │ │ └─ ml:3003 [●] │ │ │ │ ├─ adguard:53 [●] │ └───────────────────┘ │ │ │ ├─ portainer:9000[●] │ │ │ │ ├─ pdf:8080 [●] │ │ │ │ ├─ n8n:5678 [●] │ │ │ │ ├─ vault:8888 [●] │ │ │ │ └─ openclaw [●] │ │ │ └───────────────────────────┘ │ │ │ │ ● = LIVE ○ = stopped │ └────────────────────────────────────────────────────────────────────┘
The hardware
The heart of the system is an old Intel MacBook Air running Ubuntu Server headless - lid closed, display off (basically for best performance by saving resources to the max possble), running 24/7. I wrote a custom systemd service that handles the lid-close without suspending (that was a fun rabbit hole). A spare Windows laptop with an NVIDIA GPU sits alongside it on the network, but only for one job: running Immich's ML library for face and object detection and is turned on whenver there are heavy ML tasks (think of a bulk upload post vacation :) )
Benefit: No budget needed for dedicated server hardware. Approach: Repurpose what I already had. Less reliable than rack-mounted gear, sure - but the uptime has been surprisingly solid and the cost was literally zero.
What's running
Nine services, split across three categories. Everything that needs external access goes through Cloudflare Tunnels - not a single port is opened on the router. Here's what's running and the decisions behind each one.
Why I built it this way
Nothing here was accidental. These are the key architectural decisions that shaped the lab - written as comments in a config file, because honestly, that felt right for the vibe.
# /etc/homelab/decisions.conf # Last updated: Mar 2026 # ───────────────────────────────────────────────────── # [SECURITY] # WHY: Cloudflare Tunnels over Port Forwarding Zero open ports on the router. Outbound-only HTTPS connections. Free TLS, DDoS protection, and WAF. No need to touch router settings or expose home IP. # IMPACT: Zero attack surface. No firewall rules to maintain. # [PORTABILITY] # WHY: External SSD for Immich Storage Data lives independently of the host machine. Portable and upgradeable. Auto-mounts via fstab UUID. # IMPACT: Full system migration in <10 min, zero data loss. # The MacBook could be replaced without losing a single photo. # [SCALABILITY] # WHY: Offload ML to a separate GPU machine Face detection and smart search are GPU-hungry. Instead of cramming it all on the MacBook, a spare Windows laptop with an NVIDIA GPU handles just Immich's ML library over LAN. # IMPACT: MacBook CPU stays under 15%. GPU node can go # offline without breaking Immich - jobs just queue up. # [COST] # WHY: Headless MacBook as Server Custom lid-monitor systemd service turns off display when lid closes without suspending. Sleep/hibernate disabled entirely. 24/7 uptime with minimal power draw. # IMPACT: $0 hardware cost. Replaces ~$15/mo in SaaS subscriptions.
What this actually gives me
The infra is a means, not an end. Here's what it actually delivers on a daily basis.
What I actually learned
Building this was one thing. Operating it day-to-day taught me stuff no documentation ever would.
# Architecture beats policy for security Cloudflare Tunnels removed an entire class of risks by design. No firewall rules to audit, no ports to accidentally leave open. The best security rule is the one you never have to remember. # Decouple compute from storage (always) The GPU laptop can go offline and photos still load fine. The SSD can move to a new host in minutes. This separation saved me when I had to swap the MacBook's WiFi driver once. # You are the SRE team If a container crashes at 2am, it stays down until I notice. Health checks, restart policies, and Docker's restart: unless-stopped aren't nice-to-haves - they're what keeps this thing running. # Constraints are underrated No budget meant repurposing hardware I already owned. WiFi-only meant optimising for reliability over throughput. Every limitation forced a more thoughtful decision.
What's next
It works. But it's not done - and honestly, that's half the fun.
# /etc/homelab/TODO # ───────────────────────────────────────────────────── [ ] Wired ethernet over WiFi WiFi has been fine, but it's still a failure point I'd rather not have. Ethernet would especially help ML offloading latency. [ ] Proper monitoring (Prometheus + Grafana) Right now if something dies silently, I find out when I try to use it. Not great. Can't really call it production-grade without this. [ ] Automated offsite backups The SSD is portable, but portable isn't the same as backed up. Encrypted snapshots to a remote target would close this gap. [ ] Go deeper on OpenClaw (Clawdius) Turn Clawdius into a real personal agent, not just a local assistant. Next step: WhatsApp-based grocery automation (search → list → order). Also exploring PicoClaw for lightweight, always-on edge inference. [ ] Failure recovery & self-healing Right now, restarts are manual or Docker-level. Adding health checks, auto-restarts, and dependency-aware recovery would make the system resilient to partial failures.