No description
  • Shell 48%
  • HCL 46.8%
  • Just 3.4%
  • Python 1.8%
Find a file
Claudia eb5019bb1e
feat: alert when borg-backup skips a data source
The unit-failed and backup-stale rules both go green for a run that warns
and exits 0, which is exactly what the immich and vitals dumps do when they
fail — non-fatal by design so one bad source never sinks the archive. That
left a source able to stop going off-site with the whole alerting stack
green: the 19-day outage's failure mode, minus the failing unit that would
have given it away.

Matches the shared "dump failed/skipped" phrase rather than a bare
"WARNING:" — borg's own create warnings land in the same unit, and a rule
that cries wolf gets muted.
2026-08-12 08:03:44 +02:00
configs feat: alert when borg-backup skips a data source 2026-08-12 08:03:44 +02:00
docs docs: fleet networking, and why kari isn't on br0 2026-08-07 12:25:59 +02:00
machines feat: install sqlite3 on kate 2026-08-12 07:56:54 +02:00
modules comms: run the smtp relay on kylie 2026-08-07 11:50:56 +02:00
scripts monitoring: ship kate's systemd journal to loki 2026-08-07 08:55:43 +02:00
.gitignore secrets: sops+age for tfvars, encrypt tofu state; beszel token via env-file 2026-07-18 09:28:54 +02:00
.sops.yaml feat: add sops/age secrets 2026-07-18 09:14:25 +02:00
backups.tf fix: restore borg-backup's immich db password channel 2026-08-07 08:34:13 +02:00
beszel-agent.tf machines: fleet-wide provisioning — arch hosts, beszel binary, packages, media mount 2026-07-20 11:14:27 +02:00
CLAUDE.md feat!: replace wger with vitals 2026-08-06 10:01:37 +02:00
cluster.tf fix: stop self-healing error 2026-07-17 23:00:55 +02:00
justfile feat!: replace wger with vitals 2026-08-06 10:01:37 +02:00
machines.tf machines: fleet-wide provisioning — arch hosts, beszel binary, packages, media mount 2026-07-20 11:14:27 +02:00
mise.toml machines: declare sops and age on nikki, drop skopeo 2026-07-23 20:21:21 +02:00
project.tf comms: send as per-service addresses, not one shared sender 2026-08-07 11:35:31 +02:00
README.md machines: fleet-wide provisioning — arch hosts, beszel binary, packages, media mount 2026-07-20 11:14:27 +02:00
secrets.sops.yaml feat: add smtp secrets 2026-08-07 11:29:48 +02:00
terraform.tf secrets: sops+age for tfvars, encrypt tofu state; beszel token via env-file 2026-07-18 09:28:54 +02:00
variables.tf comms: send as per-service addresses, not one shared sender 2026-08-07 11:35:31 +02:00

meatbag/infra

OpenTofu IaC for a 3-node Incus cluster running OCI containers.

Architecture

                           +------------------+
                           |    Internet      |
                           +--------+---------+
                                    |
                              port forward
                                    |
                           +--------+---------+
                           |   UDM Router     |
                           | DHCP, DNS, NAT   |
                           +--+------------+--+
                              |            |
              ----------------+--        --+----------------
              |  incusVLAN       |      |  homeARPA         |
              |  10.0.100.0/24   |      |  10.0.69.0/24     |
              +--+----+----+-----+      +----+----+---------+
                 |    |    |                 |    |
               kate kylie kari           nikki  mako

Nodes

 +--------------------------------------------------+
 |                    kate (leader)                  |
 |                   10.0.100.0/24                   |
 |                                                   |
 |  proxy:    caddy, endlessh                        |
 |  media:    sonarr, radarr, lidarr, bazarr,        |
 |            prowlarr, sabnzbd, jellyfin,            |
 |            audiobookshelf, subsyt, bgutil          |
 |  web:      freshrss, homepage, dufs               |
 |  games:    valheim                                |
 |  forge:    forgejo, registry                      |
 |  monitoring: beszel, alloy                        |
 |  comms:    murmur                                 |
 |                                                   |
 |  ZFS: storage/incus, vault (2x4TB mirror)         |
 +--------------------------------------------------+

 +--------------------------------------------------+
 |                  kylie (CI)                       |
 |                 10.0.100.0/24                     |
 |                                                   |
 |  forge:      woodpecker, woodpecker-agent         |
 |  monitoring: loki, grafana                        |
 |                                                   |
 |  Podman: beszel-agent, podman-proxy (quadlet)     |
 +--------------------------------------------------+

 +--------------------------------------------------+
 |              kari (home automation)               |
 |                 10.0.100.0/24                     |
 |                                                   |
 |  home: homeassistant (VM), mosquitto, zigbee2mqtt |
 |                                                   |
 |  Podman: beszel-agent (quadlet)                   |
 +--------------------------------------------------+

 +--------------------------------------------------+
 |              nikki (management)                   |
 |                 10.0.69.0/24                      |
 |                                                   |
 |  Runs: just plan/apply/update (via SSH to kate)   |
 |  Has: incus remote configured, repo working copy  |
 +--------------------------------------------------+

Deployment flow

 nikki                        kate                      kari/kylie
 (working copy)               (tofu + state)            (remote nodes)
   |                            |                          |
   |  1. rsync working copy     |                          |
   |--------------------------->|                          |
   |                            |                          |
   |  2. ssh: tofu apply        |                          |
   |--------------------------->|                          |
   |                            |  3. incus create/update  |
   |                            |------------------------->|
   |                            |                          |
   |                            |  4. deploy-configs       |
   |                            |   (rsync + restart)      |
   |                            |------------------------->|
   |                            |                          |
   |                            |  5. deploy-machine       |
   |                            |   (apt, scripts, systemd)|
   |                            |------------------------->|

Repository structure

.
+-- terraform.tf          # Provider config, OCI registry remotes
+-- cluster.tf            # Cluster-wide Incus settings
+-- project.tf            # Instantiates all modules, passes variables
+-- variables.tf          # Image versions, MAC addresses, secrets
+-- machines.tf           # Triggers for machine config deployment
+-- terraform.tfvars      # Actual values (gitignored, contains secrets)
+-- justfile              # Command recipes
|
+-- modules/
|   +-- project/          # Utility: creates Incus project + base profile
|   +-- proxy/            # caddy, endlessh
|   +-- media/            # sonarr, radarr, lidarr, bazarr, prowlarr,
|   |                     # sabnzbd, jellyfin, audiobookshelf, subsyt, bgutil
|   +-- web/              # freshrss, homepage, dufs
|   +-- games/            # valheim
|   +-- forge/            # forgejo, registry, woodpecker, woodpecker-agent
|   +-- monitoring/       # beszel, loki, alloy, grafana
|   +-- comms/            # murmur
|   +-- home/             # homeassistant, mosquitto, zigbee2mqtt
|
+-- configs/              # Service config files (deployed via deploy-configs)
|   +-- caddy/            # Caddyfile
|   +-- alloy/            # config.alloy
|   +-- loki/             # local-config.yaml
|   +-- grafana/          # provisioning files
|   +-- mosquitto/        # mosquitto.conf
|   +-- subsyt/           # subscriptions.json
|
+-- machines/             # Host-level configs (deployed via deploy-machine)
|   +-- kate/             # (all hosts: beszel-agent binary + update timer)
|   |   +-- apt/          # sources + keyrings (debian, incus, opentofu) + packages.txt
|   |   +-- scripts/      # borg-backup, zfs-replicate, mirror-*-sync
|   |   +-- systemd/      # timers + services for above scripts
|   +-- kylie/
|   |   +-- apt/          # sources + keyrings (debian, incus) + packages.txt
|   |   +-- quadlet/      # podman-proxy
|   +-- kari/
|   |   +-- apt/          # sources + keyrings (debian, incus) + packages.txt
|   +-- nikki/            # arch fleet: pacman hooks + packages.txt, greetd;
|   +-- veronika/         # deployed from nikki (kate can't reach homeARPA)
|   +-- mako/
|
+-- scripts/
    +-- deploy-configs    # Syncs configs/ to hosts, restarts containers
    +-- deploy-machine    # Syncs machines/ to hosts (apt, scripts, systemd, quadlet)
    +-- check-updates     # Queries registries for new image digests

Commands

All commands run from the repo root on nikki:

just plan                          # tofu plan (syncs to kate first)
just apply                         # tofu apply
just check-updates                 # check registries for new image digests
just update                        # check-updates + taint changed images
just update-image <name> <module>  # force-taint and redeploy one image
just deploy-machine <host>         # deploy host configs (apt, scripts, systemd, quadlet)
just log <name> <project>          # view OCI container logs
just list-all                      # list instances across all projects
just query '{filter}'              # query Loki logs
just force-delete <name> <project> # unstick a crashed container

Adding a new service

  1. Add incus_image + incus_instance + incus_storage_volume resources in the appropriate module
  2. Add version variable to the module's variables.tf and root variables.tf
  3. Wire the variable through project.tf
  4. If the service has config files: add to configs/<service>/, add entry in scripts/deploy-configs, add terraform_data trigger resource
  5. Add image entry to scripts/check-updates IMAGES array

Adding a new machine config

  1. Create files under machines/<host>/<type>/ (apt, pacman, scripts, systemd, quadlet, greetd)
  2. Cluster hosts only: add a terraform_data trigger in machines.tf watching the fileset
  3. Run just deploy-machine <host> or (cluster hosts) let just apply pick it up

The arch fleet (nikki, veronika, mako) is deliberately absent from machines.tf: tofu runs on kate, and kate cannot reach homeARPA. Those hosts are deployed straight from nikki via just deploy-machine <host>. System-level config for them lives here; chezmoi manages only $HOME.