Impact-Site-Verification: 578d421e-1081-463d-918b-ec5e29c5b9db
<Back

Container-Based Multi-Function Home Server

Personal Project

Overview

Built a 10-container home server as Infrastructure-as-Code on a Raspberry Pi 5 (8GB RAM) with a 2TB NVMe SSD running Ubuntu Server 24.04 LTS arm64, using Docker Compose and shell scripts. Consolidates smart-home hub, Time Machine backups, web change detection, uptime monitoring, and an off-site VPN into a single box, with all configuration Git-managed so the environment can be rebuilt from scratch cheaply.

Architecture

Uses Docker Compose profiles (`phase1`–`phase4` and `full`) to bring the stack up in dependency order. During validation only the relevant phase is started; in production `full` starts everything at once. Each phase has a matching idempotent setup script, so the state can always be reproduced from the repo.

Bridge and Host networks are used deliberately for different roles. Containers with layer-2 requirements (Samba, Homebridge, Home Assistant — SMB, mDNS, Bluetooth) run on the host network; plain HTTP services are isolated on a bridge. The Homepage dashboard's `siteMonitor` resolves bridge services by container name and host services by the Docker bridge gateway IP, giving a single monitoring surface across both worlds.

Proxy hosts on Nginx Proxy Manager are configured declaratively by hitting its REST API with a Bearer token; state touched through the web UI is read back and written into `configs/`, so ad-hoc changes still land in IaC. Avahi publishes CNAMEs (`/etc/avahi/cname.d/services.conf`) that map `<service>.local -> ubuntu-pi.local` on the fly, giving every service a `.local` hostname without polluting the household DNS.

  1. phase1 platform: Nginx Proxy Manager / Docker Socket Proxy (read-only socket exposure for Homepage) / Homepage / MkDocs Material
  2. phase2 files: Samba (Time Machine with full `vfs_fruit` support)
  3. phase3 smart home: Homebridge (HomeKit + Tesla integration) / Home Assistant (`privileged` + host network + D-Bus)
  4. phase4 monitoring: Playwright Chrome (JS rendering) / Changedetection.io / Uptime Kuma
  5. Installed directly on the host: Tailscale (Exit Node) / AdGuard Home (coexists with systemd-resolved by disabling the stub listener) / Rclone (daily cron backup)

Operations & Observability

All shell scripts from `phase0-prereq.sh` through `phase4-utility.sh` are idempotent. State checks are inserted before `systemctl enable --now` and `docker compose up -d`, so re-runs only apply deltas and recovery from misconfiguration is fast.

`verify-all.sh` runs 13 health checks in one shot — port reachability, container status, mDNS resolution, SMB connectivity, Home Assistant API — making it trivial to pinpoint which layer is failing right after a deploy.

Rclone syncs `homeassistant` and `timemachine` to Google Drive at 03:00 daily, throttled to 5 MB/s to avoid saturating upstream bandwidth, giving on-box + cloud redundancy. Persistent data lives under `~/data/` (`.gitignored`), physically separated from config under `configs/` so backups never pull in source artifacts.

Hardware Tuning

Enabled PCIe Gen3 by appending `dtparam=pcie1_gen=3` to `/boot/firmware/config.txt`, roughly doubling NVMe SSD throughput (~450 MB/s -> ~900 MB/s) and effectively halving Time Machine backup times. The EEPROM was also flashed to the latest release to keep NVMe boot stable.

Because AdGuard Home needs UDP/53, the `systemd-resolved` stub listener is disabled via `/etc/systemd/resolved.conf` and `/etc/resolv.conf` is pointed at AdGuard, eliminating the port conflict between the OS default resolver and the household DNS filter.

Technologies

Raspberry Pi 5Ubuntu Server 24.04DockerDocker ComposeNginx Proxy ManagerSamba (vfs_fruit)HomebridgeHome AssistantTailscaleAdGuard HomeRcloneAvahi (mDNS)MkDocs MaterialChangedetection.ioPlaywrightUptime KumaBashIaC