Fall 2026brianmars.comBuilt at EGBT Technologies
The devbox build

Reference

One card per part of the build. Each keeps the vendor's steps apart from what this build changes, and lists where it breaks against the rest.

Metal and OS

Part 1
  1. Hardware note: Pinova P2 M.2 slots

    The two M.2 slots in this mini PC are not the same, and the case opens with a latch the manual doesn't mention.

    Verified 17 Oct 2026
  2. Debian 13, headless, on two disks

    A minimal Debian install with no desktop, on the OS disk only, leaving the data disk untouched for later.

    Verified 17 Oct 2026
  3. Base hardening

    Updates, baseline tools, key-only SSH, a UTF-8 locale, and automatic security updates on a fresh Debian box.

    Verified 17 Oct 2026
  4. Data volume

    The second disk as one ext4 partition, mounted at /srv by UUID, holding stacks, container data and backups.

    Verified 17 Oct 2026

Reachable from anywhere, visible to no one

Part 2
  1. Tailscale on a server

    Tailscale for a stable address on any network, with MagicDNS on, key expiry off, and Tailscale SSH left out on purpose.

    Verified 17 Oct 2026
  2. UFW, tailnet only

    A firewall that answers only on the Tailscale interface, with one narrow exception for containers reaching the front door.

    Verified 17 Oct 2026
  3. mosh and tmux

    A connection that survives wifi changes and laptop sleep, and a shell that survives the connection.

    Verified 17 Oct 2026

Containers and a front door

Part 3
  1. Docker Engine on Debian

    Docker Engine and the Compose plugin on a headless Debian 13 box, with storage on a separate data volume and DNS that works behind Tailscale.

    Verified 17 Oct 2026
  2. Caddy with Cloudflare DNS-01

    One HTTPS front door with real certificates, for private service names that only lead somewhere from inside the tailnet.

    Verified 17 Oct 2026

Source control and CI

Part 4
  1. Forgejo

    A private git host in one container, behind Caddy for the web and on its own port for git over SSH.

    Verified 17 Oct 2026
  2. Forgejo Actions runner with dind

    A CI runner whose job containers reach Forgejo at the same address the runner does, from inside a nested Docker daemon.

    Verified 17 Oct 2026

Watching it and keeping it

Part 5
  1. Dockge

    A web UI for the compose stacks under /srv/stacks, working through the Docker socket.

    Verified 17 Oct 2026
  2. Beszel

    Resource graphs for the box and its containers, from a hub and an agent that both run on host networking.

    Verified 17 Oct 2026
  3. Uptime Kuma

    Up and down checks for every service, probing 127.0.0.1 from host networking, with alerts that actually arrive.

    Verified 17 Oct 2026
  4. Homepage

    One landing page with a tile per service and live container status, configured from files you own.

    Verified 17 Oct 2026
  5. restic to SFTP and R2

    Encrypted daily backups to a machine at home and to object storage off-site, run as root by a systemd timer, with a tested restore.

    Verified 17 Oct 2026
  6. msmtp and failure alerts

    One outbound mail account for Forgejo, Uptime Kuma, and an email whenever a systemd job fails.

    Verified 17 Oct 2026
Placeholders

Every card uses the same stand-ins. Swap in your own.

devbox.example.ts.net
The box on your tailnet. example.ts.net is the sample tailnet in Tailscale's docs.
100.x.y.z
The box's tailnet address, from tailscale ip -4.
tailnet.example
A domain you own, on public DNS, for the private service names. Caddy needs a real zone for its certificates.
git.dev.tailnet.example
A service name under the *.dev wildcard record, which points at 100.x.y.z.
youruser
Your login on the box.
you@tailnet.example
Your mail address. Alerts go here.
backupuser@backup-host.local
The home machine that holds the local backup copy.