← Projects

Dazoo

A tour of the self-hosted platform that runs my photos, my media, some work pipelines, and (increasingly) the laptop I'm typing this on.

dazoo repo
Forgejo
via Butane / Ignition
Always-on layer
  • mesh, git, front door
  • secrets, observability
  • offsite backup
via Flux GitOps
Talos clusters
  • work: airbyte, lotus-lake
  • home: immich, media, homepage
via OCI image, chezmoi
Workstation
  • Fedora Sway Atomic
Every layer rebuildable from the same repo. All nodes joined on a Headscale mesh.

Why I built this

The pitch is boring on purpose: declarative everything, one repo, rebuildable from git at every layer. If a machine catches fire I want to be able to point at a commit hash and walk away. The reward for boring is that the interesting stuff (the apps, the data, the photos) gets to stay interesting.

The repo is called dazoo. It declares three things: a handful of cloud VPSes, two Kubernetes clusters, and a workstation. Three deployment mechanisms, one source of truth.

The always-on layer

A small set of cloud nodes that have to stay up no matter what else is happening. They run the mesh coordinator, self-hosted git, the secrets vault, and the front door (DNS, TLS, tunnels). Everything else in the system depends on them, so they get to be simple and unglamorous: Fedora CoreOS, containers as systemd units, no orchestrator.

Choosing CoreOS here was deliberate. These nodes don't need a scheduler, they need to come back up the same way they went down. Auto-updating, immutable, configured from a single Butane file in the repo.

The work cluster

Real Kubernetes for the things that actually want it: data ingestion, app workloads with their own deploy cadence, anything that benefits from auto-healing and zero-downtime rollouts. Talos for the OS (no SSH, no shell, configured from yaml) and Flux for the GitOps loop.

The shape of a deploy is the satisfying part. I push code, the app's own CI builds an image and bumps a tag in its deploy/ directory, Flux notices, and the cluster converges. I don't touch prod. If I find myself running kubectl apply something has gone wrong upstream and that's the bug to fix.

  1. git push
  2. Forgejo
  3. Flux
  4. Talos cluster
  5. running pod
I don't touch prod. Push to Forgejo, Flux pulls, the cluster converges.

The personal cluster

Same pattern, different priorities. The work cluster optimizes for “quietly correct.” This one optimizes for “mine.” It runs Immich for photos, the usual media stack (Jellyfin, the *arrs, qBittorrent behind a VPN gateway), and a homepage that pulls everything together.

It's storage-heavy and latency-tolerant in ways the work cluster isn't, which lets it be a good place to try things. Most of what eventually graduates to the work cluster gets its first real test here.

Monkey works

The workstation is where the same idea lands at the desk. Fedora Sway Atomic for the base, a custom OCI image layered on top for the packages I always want, and chezmoi pulling dotfiles from the same git system everything else lives in. The long-term goal is the obvious one: laptop, servers, and clusters all the same kind of thing, all rebuildable from the same repo on the same afternoon.

That last bit is the whole point. The boring infra is the interesting infra. Everything else is just what runs on top of it.