Infrastructure / automation

HomeLab & automation

Self-hosted production platform on residential hardware: deploys, mail, analytics, monitoring, and the reliability work that keeps it up.

  • Docker Compose
  • Traefik
  • Cloudflare Tunnel
  • Gitea
  • FastAPI
  • systemd
  • Home Assistant
  • Tailscale
  • MinIO
  • Uptime Kuma
  • Umami
  • OpenClaw
  • MCP
  • ESPHome
Self-hosted production platform Public ingress through a tunnel rather than an open port. One proxy fronts every service, so services changed machines without touching DNS. WEB, AGENTS AND PRINTERS MEDIA, BUILDS AND APIS MOVED, NO DNS CHANGE EDGE Cloudflare tunnel no WAN ports open Traefik one route file Sites + PWAs prerendered MCP fleet · CRM · HA agents and printers Ember API Kotlin and Ktor Media stack Quick Sync transcode Routes every service Outside the host Service
Public traffic enters through a tunnel rather than an open port. Traefik is highlighted because every public service sits behind one route file, which is how services changed machines with no DNS change and no downtime.

Overview

A personal platform on residential hardware, not a demo on SaaS, and honestly a homelab: it runs Daniel’s own sites, tools and agents. It began on a single 2012 laptop, grew to two machines in August 2026, and in October 2026 moved entirely onto a small desktop, with the laptop retired. A second building adds a remote printer farm over a private mesh. The constraint is the interesting part: consumer hardware makes every reliability decision explicit rather than something a datacentre absorbs.

Problem

Running real services from a home network means a dynamic IP, no port forwarding, residential mail rejection, and hardware that powers off. Cloud CI runners add cost and maintenance. But the harder problem is the one that only shows up after months of uptime: on constrained hardware, unrelated services quietly compete for memory, boot-time protections silently fail to persist, and alerting paths break without ever changing state, so the system looks healthy right up until it is not.

Solution

A tunnel for public ingress with no WAN ports open. GitHub stays the source of truth; Gitea mirrors repositories and webhook-driven scripts build in throwaway containers and publish static sites, serialized behind a lock so two pushes cannot race. Postfix relays through a transactional provider for deliverability. systemd units own boot order, a hardware watchdog, and an automated reboot that asks the printers for permission first. Health checks understand printer power state, so a machine that is deliberately off does not page anyone.

Architecture

The main host runs Traefik with per-service Docker label routing, nginx for static sites, Gitea with self-hosted Actions runners, the contact and chat API, Postfix, Home Assistant with ESPHome devices, Uptime Kuma, self-hosted Umami analytics, a self-hosted CRM, the MCP server fleet, an agentic Telegram gateway, a Firefly III ledger, the media stack with hardware transcoding, the Android build toolchain and two Klipper printers. A site in another building adds six printers over a private mesh. A push to master triggers mirror sync, a build in a throwaway container, and a publish to the static site root. Operations are watched through a read-only MCP server (agent sessions, services, journals, disk and memory, containers) where every call is a fixed argument list with no shell, plus a phone dashboard in Home Assistant that shows what each coding agent is doing and what is waiting on a person.

Key decisions

A tunnel over port forwarding, so nothing is exposed by an open port, and pointed at the proxy rather than at any individual service. That indirection is what let a public API move to a different machine with no tunnel change, no DNS change and no downtime: the proxy grew one route entry pointing at the other host over the LAN, which container labels cannot express because they only describe local containers. Per-service Docker labels for everything that does stay local. Self-hosted CI over cloud runners. Builds in ephemeral containers, because the hosts deliberately carry no Node or Python toolchain. Isolated compose projects per service, since a shared project name makes a routine teardown command destructive to unrelated stacks. Latency-critical processes pinned out of swap rather than swap disabled, because removing swap outright converts a stall into an out-of-memory kill.

Challenges

The instructive failures were all silent. Two deploys landing seconds apart let one delete the working tree out from under the other, and it surfaced as a build error naming a file that plainly existed, which sends you debugging the application instead of the pipeline. A hardware watchdog looked correctly configured and had not survived a single reboot, because the distribution deny-lists that module in a per-kernel file and the check had only ever been run in the same session as the fix. A monitoring dashboard reported every public site down while all of them were up, because the checks ran from inside the same network and could not reach the edge addresses serving its own zone. Each one taught the same lesson: verify a persistence change after a reboot, and verify a failure path from outside the system that owns it.

Outcome

A live portfolio and API on theevolvedalligator.com, automated deploys on every push, a box that self-recovers from the class of hang that used to take it down for hours, and a second machine absorbed without a single public URL changing. More to the point, it is where the operational instincts come from: the habit of asking how a system fails quietly, of distrusting a green check that was never tested against a restart, and of writing the reasoning down where the next person can find it. The move to new hardware in October 2026 kept that standard: the public edge switched with zero downtime by running a second tunnel connector and comparing every hostname before cutting over, Home Assistant moved only while the printers were idle, the git server was offline for 26 seconds, and the old machine was retired only after a go/no-go audit.

Wondering if Daniel is a fit? Ask his assistant, hiring, contracts, or collaboration.