Commit graph

1,013 commits

Author SHA1 Message Date
9024d41230 Add Grafana alerts dashboard for mobile-friendly alert overview
Two panels: currently firing alerts (firing/pending/noData/error) and
recent state changes. Refreshes every 30s. Uses Grafana's built-in
alertlist panel type — no datasource needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:16:54 -07:00
9efe5c97fe Fix authentik worker OOMKill: limit concurrency to 2
Dramatiq defaults to one worker process per CPU core. On ringtail (16 cores)
this spawned 16 processes, each loading the full Django app, exceeding the
1Gi memory limit and causing a crash loop (228 restarts over 7 days).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:05:16 -07:00
bd0ff30d3f Unify container build workflows (#306)
All checks were successful
Build Container / detect (push) Successful in 3s
## Summary
- Merges `build-container.yaml` and `build-container-nix.yaml` into a single workflow
- Detect job classifies each changed container by presence of `Dockerfile` and/or `default.nix`
- Dockerfile containers build on `k8s` (indri) via Dagger; Nix containers build on `nix-container-builder` (ringtail) via nix-build + skopeo
- Containers with both build files (alloy, nettest, ntfy) get built on both runners

## Test plan
- [ ] Push a change to a Dockerfile-only container (e.g. grafana) — verify it builds on k8s only
- [ ] Push a change to a nix-only container (e.g. jobsync) — verify it builds on nix-container-builder only
- [ ] Push a change to a dual container (e.g. ntfy) — verify it builds on both runners
- [ ] Test workflow_dispatch with a specific container name

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #306
2026-03-23 20:55:50 -07:00
4cc26ed5eb Update ntfy tag to main build v2.19.2-d1dac0c-nix
C0 fix-forward: switch from branch-built image to main-built image.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:36:34 -07:00
d1dac0c241 Upgrade ntfy v2.17.0 → v2.19.2 (#305)
All checks were successful
Build Container (Nix) / detect (push) Successful in 1s
Build Container / detect (push) Successful in 3s
Build Container (Nix) / build (ntfy) (push) Successful in 4s
Build Container / build (ntfy) (push) Successful in 11s
## Summary
- Upgrade ntfy from v2.17.0 to v2.19.2
- Update Dockerfile and Nix build definitions with new version, commit SHA, and hashes
- Add `subPackages = [ "." ]` to Nix build to handle new `tools/loadtest` module in upstream

## Upstream changes (no breaking changes)
- **v2.18.0:** Experimental PostgreSQL backend support
- **v2.19.0:** PostgreSQL read replica support, notification sound throttling
- **v2.19.1-2:** PostgreSQL bug fixes, web push race condition fix

## Test plan
- [ ] Container builds complete on Forgejo Actions (both Dockerfile and Nix)
- [ ] Update kustomization.yaml `newTag` to the built nix image tag
- [ ] `argocd app set ntfy --revision upgrade/ntfy-v2.19.2 && argocd app sync ntfy`
- [ ] Verify ntfy health: `curl https://ntfy.ops.eblu.me/v1/health`
- [ ] Send a test notification

Reviewed-on: #305
2026-03-23 10:32:06 -07:00
06e721841c Review 12 reference docs: fix stale image refs, expand stubs, add cross-refs
Replace hardcoded image tags in Quick Reference tables with pointers to
kustomization manifests (tags drift with every container release). Fix
Prometheus CNPG scrape target, remove misleading .ts.net URLs, expand
external-secrets stub, add backup/disaster-recovery cross-references.
Limit doc-reviewer agent to one doc per cycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:51:57 -07:00
3750428b58 Fix ArgoCD apps app permanent OutOfSync
Remove `group: ""` from ignoreDifferences in tailscale-operator and
tailscale-operator-ringtail — ArgoCD normalizes away the empty string
field, so the live state never matches git.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:42:37 -07:00
044ad7dad7 Revert fly/start.sh to polling loop — tailscale wait needs v1.96.2+
All checks were successful
Deploy Fly.io Proxy / deploy (push) Successful in 1m33s
The Fly container pulls from tailscale/tailscale:stable which is still
v1.94.2. The `tailscale wait` command doesn't exist until v1.96.2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:44:47 -07:00
e9b8e3d80b Revert Tailscale operator to v1.94.2 — images not yet published
v1.96.3 exists as a GitHub release but Docker Hub images for both
tailscale/tailscale and tailscale/k8s-operator haven't been published
yet (v1.94.2 is still latest). Revert the image tags; the fly/start.sh
`tailscale wait` improvement and review date stamps are retained.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:41:40 -07:00
2e46f99820 Upgrade Tailscale operator v1.94.2 → v1.96.3 (#304)
Some checks failed
Deploy Fly.io Proxy / deploy (push) Failing after 7m0s
## Summary

- Bump Tailscale operator, proxy containers, and init containers from v1.94.2 to v1.96.3 across both clusters (indri + ringtail via shared base kustomization)
- Replace hand-rolled `until tailscale status` polling loop in `fly/start.sh` with `tailscale wait --timeout 60s` (new in v1.96.2)
- Stamp kube-state-metrics review date (already current at v2.18.0)

## Notable upstream changes (v1.94.2 → v1.96.3)

- Go upgraded from 1.25 to 1.26
- `tailscale wait` command — blocks until daemon is running + interface has IP
- AuthKey policy now applies only when users are not logged in (behavioral change)
- Peer Relay improvements (metrics, EC2 IMDS, UDP socket scaling)
- UPnP stability fixes

## Deploy plan

1. Merge PR
2. Sync tailscale-operator on indri: `argocd app sync tailscale-operator`
3. Sync tailscale-operator on ringtail: `argocd app sync tailscale-operator-ringtail --server ringtail...`
4. Verify proxy pods roll with new image: `kubectl --context=minikube-indri -n tailscale get pods`
5. Verify ingress connectivity (spot-check a few `*.tail8d86e.ts.net` services)
6. Rebuild + deploy Fly proxy container (separate step, picks up `tailscale wait` change)

## Test plan

- [ ] ArgoCD diff looks clean for both apps before sync
- [ ] Proxy pods on indri come up healthy with v1.96.3 images
- [ ] Proxy pods on ringtail come up healthy with v1.96.3 images
- [ ] Tailscale ingress services remain reachable (e.g., grafana, prometheus)
- [ ] Fly proxy rebuild deploys successfully with `tailscale wait`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #304
2026-03-22 19:31:22 -07:00
Forgejo Actions
262299c82a Update docs release to v1.14.3
- Built changelog from towncrier fragments

[skip ci]
2026-03-22 18:20:41 -07:00
2cb0ce4289 Review and correct Tailscale reference doc v1.14.3
Fix wrong ACL path, add missing device tags (ringtail, per-service tags,
ci-gateway, flyio-proxy), correct access matrix (PyPI→DevPI, homelab
grants), add homelab→homelab SSH rule, document auto approvers section,
and add last-reviewed frontmatter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 18:18:45 -07:00
f7221f3990 Update ringtail flake inputs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 17:57:54 -07:00
6d65e6928c C2: Deploy infrastructure alerting pipeline (#303)
## Summary

Mikado chain to replace `mise run services-check` with Grafana Unified Alerting backed by ntfy push notifications.

**Design:**
- Grafana Unified Alerting evaluates rules against Prometheus/Loki
- ntfy webhook contact point delivers iOS notifications
- Anti-noise policy: page once per 24h per alert group
- Every alert links to a runbook in `docs/how-to/alerts/`
- services-check eventually queries the alerting API instead of doing its own probes

**Chain (bottom-up):**
1. `configure-grafana-alerting-pipeline` — enable alerting, ntfy contact point, notification policy
2. `first-alert-and-runbook` — end-to-end proof of concept with blackbox probe failure
3. `port-services-check-alerts` — migrate all services-check probes to alert rules + runbooks
4. `refactor-services-check-to-query-alerts` — rewrite services-check to query Grafana API
5. `deploy-infra-alerting` — goal card

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #303
2026-03-22 14:52:56 -07:00
f1620abb17 Improve Frigate health checks to catch NFS and camera failures
Replace single aggregate camera_fps check with per-camera FPS validation
and NFS storage accessibility check. Motivated by an outage where Frigate
API responded OK but NFS mount was inaccessible, causing "no frames" in UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 09:55:53 -07:00
dcab489b60 agent memory ignore 2026-03-21 19:03:21 -07:00
810340a328 Update service-versions.yaml for loki 2026-03-20 16:10:19 -07:00
531a49abeb C0 update deployment for loki to 3.6.7 2026-03-20 16:06:29 -07:00
f9426b734c Update loki to 3.6.7 (#302)
All checks were successful
Build Container (Nix) / detect (push) Successful in 1s
Build Container / detect (push) Successful in 2s
Build Container (Nix) / build (loki) (push) Successful in 1s
Build Container / build (loki) (push) Successful in 6s
Reviewed-on: #302
2026-03-20 16:02:28 -07:00
0f0ee2a319 Update docs and kiwix kustomization tags to 613f05d builds
Also catches kiwix's transmission sidecar up from v4.0.6-r4 to
v4.1.1-r1, matching the torrent service (upgraded in PR #282 but
the kiwix sidecar was missed). No breaking changes — old RPC
protocol is supported through 4.x.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 06:40:49 -07:00
3d2a97aaf9 Update kustomization tags to OCI-labeled builds (613f05d)
Point all services at the 613f05d images which carry the new
consistent OCI labels. Skipped kiwix/transmission (old v4.0.6-r4
version, no matching build) and docs/quartz (no 613f05d build).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 06:34:12 -07:00
613f05dfde Add consistent OCI labels to all container Dockerfiles
All checks were successful
Build Container (Nix) / build (miniflux) (push) Successful in 2s
Build Container (Nix) / build (navidrome) (push) Successful in 2s
Build Container / build (devpi) (push) Successful in 41s
Build Container (Nix) / build (nettest) (push) Successful in 15s
Build Container / build (grafana-sidecar) (push) Successful in 1m27s
Build Container / build (grafana) (push) Successful in 3m23s
Build Container (Nix) / build (ntfy) (push) Successful in 3m19s
Build Container (Nix) / build (prometheus) (push) Successful in 1s
Build Container (Nix) / build (quartz) (push) Successful in 1s
Build Container (Nix) / build (runner-job-image) (push) Successful in 1s
Build Container (Nix) / build (teslamate) (push) Successful in 2s
Build Container (Nix) / build (transmission) (push) Successful in 2s
Build Container (Nix) / build (transmission-exporter) (push) Successful in 1s
Build Container (Nix) / build (unpoller) (push) Successful in 1s
Build Container / build (kiwix-serve) (push) Successful in 1m17s
Build Container / build (kubectl) (push) Successful in 41s
Build Container / build (homepage) (push) Successful in 8m21s
Build Container / build (mealie) (push) Successful in 1m1s
Build Container / build (loki) (push) Successful in 8m21s
Build Container / build (miniflux) (push) Successful in 2m24s
Build Container / build (nettest) (push) Successful in 14s
Build Container / build (ntfy) (push) Successful in 8m33s
Build Container / build (prometheus) (push) Successful in 37s
Build Container / build (quartz) (push) Successful in 19s
Build Container / build (navidrome) (push) Successful in 10m36s
Build Container / build (runner-job-image) (push) Successful in 3m18s
Build Container / build (transmission) (push) Successful in 20s
Build Container / build (transmission-exporter) (push) Successful in 21s
Build Container / build (unpoller) (push) Successful in 11s
Build Container / build (teslamate) (push) Successful in 4m42s
Every container now carries title, description, version, source, and
vendor labels per the OCI image spec. Version is derived from the
existing CONTAINER_APP_VERSION ARG at build time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 20:42:00 -07:00
c92b949a20 Fix UID sed to target root-level dashboard uid only
The top-level "uid" in Grafana dashboard JSON is at 2-space indent
near the end of the file, not the first occurrence. Match on ^  "uid"
to avoid clobbering nested datasource uid references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:56:50 -07:00
334fbbb9e3 Fix TeslaMate/UnPoller dashboard UID sed clobbering datasource refs
The previous sed replaced ALL "uid" fields in dashboard JSON files,
including datasource references inside panels, causing dashboards to
go dark. Scope the replacement to only the first occurrence (the
top-level dashboard UID) using GNU sed 0,/pattern/ addressing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:53:00 -07:00
6f88baeb91 Fix Grafana starred dashboards lost on pod restart
Add init container to pre-populate ConfigMap dashboards before Grafana
starts, eliminating the race between the sidecar and the provisioner
that caused dashboard DB records to be deleted and re-created with new
IDs. Also stamp stable UIDs on TeslaMate and UnPoller dashboards
fetched from upstream.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 12:40:44 -07:00
0dffdb9974 Add Claude Code subagents for infrastructure workflows
Four project-scoped subagents that formalize existing mise task
workflows as constrained, specialized AI agents:
- infra-health: background health monitor (wraps services-check)
- doc-reviewer: persistent-memory documentation reviewer
- change-classifier: C0/C1/C2 triage before work begins
- mikado-navigator: C2 chain state advisor (wraps docs-mikado)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:57:36 -07:00
ef8c2118a1 Standardize USAGE pragmas and typer parsing across mise tasks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:42:01 -07:00
86220b7b88 Update Prometheus deployment to v3.10.0-0d27797
C0 fix-forward: update kustomization newTag and mark service reviewed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:46:07 -07:00
0d2779762a Upgrade Prometheus to v3.10.0 (#301)
All checks were successful
Build Container (Nix) / detect (push) Successful in 2s
Build Container / detect (push) Successful in 2s
Build Container (Nix) / build (prometheus) (push) Successful in 2s
Build Container / build (prometheus) (push) Successful in 44m11s
## Summary
- Bump Prometheus from v3.9.1 to v3.10.0 in custom container Dockerfile
- v3.10.0 adds distroless Docker image variants, new PromQL `fill` operators, and performance improvements
- Dagger build tested locally — builds cleanly

## Remaining after merge
- Update `kustomization.yaml` newTag with the auto-built image tag
- Update `service-versions.yaml` (last-reviewed + current-version)
- ArgoCD sync

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #301
2026-03-18 07:47:46 -07:00
528d3da327 Review power.md: add ringtail, mark reviewed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:37:31 -07:00
e0dbcbd997 Update retention changelog to reflect final PVC decision
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:46:55 -07:00
21ddc74cdc Revert PVC size changes, add hostpath comment
StatefulSet volumeClaimTemplates are immutable and minikube's hostpath
provisioner doesn't enforce PVC size limits anyway. Add comments noting
the data grows freely on the 1.8TB backing disk.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:46:17 -07:00
ef199b70f0 Increase Prometheus and Loki data retention
Prometheus: 15d → 10y (3650d), PVC 20Gi → 200Gi
Loki: 31d (744h) → 365d (8760h), PVC 20Gi → 50Gi

Indri has 1.6 TB free on the minikube backing disk — the previous
15-day Prometheus retention was losing valuable long-term metrics data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:44:00 -07:00
50d3b3b21e Rename Borgmatic to Borg Backups on Homepage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:34:13 -07:00
e8bdecdb11 Rename Borgmatic dashboard to Borg Backups, add duration graph
Rename dashboard title since borgmatic is just the execution layer.
Add Backup Duration Over Time panel next to New Data Per Backup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:33:27 -07:00
8425f56dc3 Add Fly.io dashboard to Homepage admin bookmarks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:29:44 -07:00
64afd40a29 Fix Grafana widget fields (lowercase) and hide Miniflux read count
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:28:41 -07:00
98584d0d67 Trim Homepage widget metrics for cleaner layout
- Forgejo: show only notifications and pull requests
- Jellyfin: show only movies/series/episodes, hide now playing
- Grafana: hide data sources, show dashboards and alerts only

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:26:15 -07:00
443e090ec6 Enable equal height tiles in Homepage groups
Add useEqualHeights: true so service tiles within each row expand to
match the tallest tile, fixing uneven layout from widget metrics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:23:53 -07:00
b0ce9be30b Fix Homepage layout: use row style with columns for full-width groups
style: row makes each group span the full page width (one per row),
while columns: 4 tiles services horizontally within each group.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:21:44 -07:00
816fd552f0 Set Homepage to single-column group layout
Add maxGroupColumns: 1 so each category gets its own full-width row,
with service tiles arranged side-by-side within each group.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:19:40 -07:00
96d0f668fd Reorganize Homepage groups: add Home, move Grafana to Infrastructure
Move NVR, Jellyfin, and DJ to new Home group. Move Grafana from Content
to Infrastructure. Switch all layout groups from column to row style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:15:52 -07:00
3e9873d669 Fix borgmatic backup: use correct kubectl context on indri
The Mealie SQLite dump hook used `minikube-indri` (the context name on
gilbert), but on indri itself the context is just `minikube`. This caused
the before_backup hook to fail, aborting all backups since the hook was added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 06:07:44 -07:00
cfe3391f1a Bump Frigate retention and add recording health check
Increase retention: continuous 3→180d, detections 14→30d, alerts 30→730d.
Plenty of NFS headroom (~9.4 TiB free, ~6.6 GB/day for one camera).

Add frigate-recording check to services-check that verifies camera_fps > 0,
which would have caught the 6-day outage from the mqtt config removal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:24:11 -07:00
6617e44e5b Fix Frigate crash: re-add required mqtt config section
Frigate's config schema requires an `mqtt` field even when MQTT isn't
used. Commit 40f1568 removed it along with Mosquitto, causing Frigate
to fail validation on startup. Add `mqtt.enabled: false` to satisfy
the schema without needing a broker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:10:23 -07:00
4f99b7edaa Update alloy kustomizations to local container tags
Point alloy-k8s at v1.14.0-61f02a0 (Dockerfile) and both ringtail
deployments at v1.14.0-61f02a0-nix (Nix build).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:55:55 -07:00
61f02a0335 Localize Alloy container image (#300)
All checks were successful
Build Container (Nix) / detect (push) Successful in 2s
Build Container / detect (push) Successful in 2s
Build Container (Nix) / build (alloy) (push) Successful in 14s
Build Container / build (alloy) (push) Successful in 38m34s
## Summary

- Add `containers/alloy/` with dual Dockerfile + Nix build files for Grafana Alloy v1.14.0
- Both builds fetch source from forge mirror (`forge.ops.eblu.me/mirrors/alloy.git`), build the web UI (Node), then compile the Go binary with `netgo embedalloyui` tags
- Update all three alloy deployments (alloy-k8s, alloy-ringtail, alloy-tracing-ringtail) to use `registry.ops.eblu.me/blumeops/alloy`
- `promtail_journal_enabled` tag omitted — requires systemd headers and none of our configs use `loki.source.journal`

## Build verification

- **Dockerfile:** Tested locally via `docker build`, binary reports `v1.14.0` with correct tags
- **Nix:** Tested on ringtail via `nix-build`, all three hashes (fetchgit, npmDeps, goModules) resolved and build succeeds

## Post-merge steps

1. Wait for CI to build the container from main (both Dockerfile and Nix workflows)
2. `mise run container-list alloy` to find the `[main]` tagged image
3. C0 follow-up to update `newTag` in all three kustomizations from `v1.14.0-placeholder` to the real tag
4. Sync ArgoCD apps and verify pods come up healthy

Reviewed-on: #300
2026-03-17 16:42:53 -07:00
Forgejo Actions
cdba9dca96 Update docs release to v1.14.2
- Built changelog from towncrier fragments

[skip ci]
2026-03-17 13:24:13 -07:00
1f000c8e39 Add last-updated subsort to docs-review, review gilbert card v1.14.2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:22:01 -07:00
995478b91f Review jellyfin and automounter services
Both services current: jellyfin 10.11.6 (latest upstream),
automounter 1.11.0 (Mac App Store). Add missing frigate share
to automounter docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:06:23 -07:00