bash — christopher@homelab

christopher@homelab:~$ whoami

christopher. I build and run a homelab at home

christopher@homelab:~$ cat mission.txt

christopher@homelab:~$ _

bare metal, always on 12+ services in Docker DNS · VPN · monitoring

# architecture

How traffic gets to the static site, the live terminal, and everything running on the box at home.

Visitors browser Cloudflare Pages static portfolio christopher-lab.pages.dev afraid.org URL forward → Pages CF Quick Tunnel WebSocket proxy terminal-gateway Homelab · 192.168.1.237 Caddy reverse proxy · TLS Docker services 12+ containers Tailscale mesh VPN · exit node AdGuard + Unbound recursive DNS Minecraft Fabric · BlueMap terminal-gateway read-only shell Monitoring stack Uptime Kuma · Beszel · Dozzle HTTPS WSS
static site path live terminal path homelab services

# how this site works

A quick case study on shipping a public portfolio when your ISP won't let inbound ports through.

Problem

I wanted a public portfolio that shows off the homelab (live status, a read-only terminal, the whole thing), but my ISP blocks inbound ports. I can't just point a domain at my house and call it a day.

Approach

I put the static site on Cloudflare Pages (free, fast, no server to babysit). My afraid.org subdomain forwards to Pages so I get a clean URL. For the live terminal, a Cloudflare quick tunnel on the homelab exposes a WebSocket to a read-only gateway with sensitive paths blocked and output redacted. Status syncs from Uptime Kuma on a cron job.

Outcome

Public site with zero open inbound ports. Visitors get HTTPS, live monitoring, and a sandboxed shell peek at the stack. Deploys are a script on the homelab that pushes to Pages. When the box reboots, Docker brings everything back in about two minutes.

# the stack

All of this runs in Docker on one machine at my place. Caddy sits in front and handles HTTPS.

AdGuard Home + Unbound

I use AdGuard Home with Unbound for network-wide blocking and recursive DNS. Queries go to the root servers, not some upstream resolver.

DNSPrivacyUnbound

Encrypted DNS

DoH, DoT, and DoQ endpoints so devices can resolve DNS privately. Works off-network too when I'm on the VPN.

DoHDoTDoQ

Tailscale

Tailscale mesh VPN with an exit node and MagicDNS. I can reach everything at home from anywhere without opening ports.

WireGuardExit nodeMagicDNS

SearXNG

Self-hosted SearXNG for search. No tracking, no profile building, just aggregated results.

SearchPrivacy

Caddy

Caddy as reverse proxy and TLS. Let's Encrypt certs via DNS-01, so every service gets a clean HTTPS URL.

Reverse proxyLet's Encrypt

Monitoring

Uptime Kuma for uptime, Beszel for metrics, Dozzle for container logs, and a speedtest tracker so I know when the ISP is acting up.

Uptime KumaBeszelDozzle

Homepage

Homepage dashboard: one page with links and widgets for everything on the network.

Dashboard

Minecraft + BlueMap

Fabric Minecraft server (The Boys) with Chunky pregen and BlueMap for a live web map of the world.

FabricBlueMapRCON

# explore the stack

Read-only shell into /opt/stacks and Minecraft config over a Cloudflare Tunnel. You can't edit anything. Sensitive paths are blocked and output gets redacted.

ssh — christopher@homelab (read-only) waiting…

# live status

Pulled live from my Uptime Kuma instance. Updates every minute.

checking…
Connecting to monitoring…

# reliability

What happens when the box reboots, how status stays fresh, and what I actually monitor.

live metrics

  • Host uptime
  • Last deploy
  • Last incident
  • Status synced

runbook

  • On reboot: systemd starts Docker, compose stacks come up in order (DNS → proxy → services). Caddy picks up certs, tunnels reconnect automatically.
  • Status sync: fetch_status.py runs every minute via cron. It pulls Uptime Kuma heartbeats, host uptime, deploy timestamp, and incident log into status.json, then deploys if the hash changed.
  • Monitoring: Uptime Kuma watches HTTP endpoints and TCP ports. Beszel tracks CPU/RAM/disk. Dozzle for container logs. I get alerts when something stays down.

# the hardware

No datacenter required. This whole stack runs on a recycled office desktop in my house.

running now

  • HostDell OptiPlex 780 SFF
  • CPUIntel Core 2 Duo E8400 @ 3.0 GHz
  • Memory16 GB DDR3
  • Storage500 GB drive
  • OSLinux · Docker · Caddy
  • NetworkTailscale mesh + recursive DNS

roadmap

  • ComputeIntel N305 mini PC (8-core)
  • Rack10" DeskPi RackMate T0
  • SwitchTP-Link TL-SG108PE (PoE+)
  • CamerasReolink PoE + Frigate NVR
  • PowerUPS battery backup
  • GoalVLAN segmentation + local AI

# about

I run a homelab at home: DNS, VPN, reverse proxy, monitoring, Docker containers, a Minecraft server. I set it all up and maintain it myself on hardware that's on 24/7 in my house.

Most of what I know about infra and Linux I picked up by actually running it, not just reading docs.

I'm available for contract work on homelab setup, Docker, reverse proxies, DNS/VPN, and self-hosted projects. If you want help standing up something similar, reach out on LinkedIn.