Files
esh-pfi-infrastructure/stacks/uptimekuma/compose.yaml
T
vh 94899d6fa3 feat(uptimekuma): normalize names off Homepage, publish the status page, restore the widget
NAMES. Homepage already answers "what is this service called", so the monitor
name is now that name verbatim -- a second naming authority is how drift starts,
and an alert reading "[Uptime Kuma] Beszel hub is DOWN" sends you hunting for a
card that does not exist. Only two rows moved (Beszel hub -> Beszel, Dozzle hub
-> Dozzle); the " hub" suffixes were mine, not the services'.

The remaining mixed case is deliberate and is now documented as such. talk, vor
and task-board are lowercase on Homepage and in their own repos; title-casing
them here would make this board disagree with both. What actually looked messy
was scripts/kuma's own ASCII-ordinal sort, which buried every lowercase name
below every capitalised one. Fixed to case-insensitive.

⚠ RENAME SAFETY, which this pass needed and did not have. The seed keys on NAME,
so editing a name would have read as a brand-new monitor: added fresh, with the
old row orphaned, still checking, still alerting, and holding all the history.
`rename_from:` names the old row for one run. Verified: both renamed monitors
kept their IDs and all 67 heartbeats.

Added with it, an orphan warning for any row on the board the spec no longer
names -- because a forgotten monitor keeps paging. Its first cut diffed against
the PRE-EDIT snapshot and so cried wolf on its own successful renames; it
re-reads the board now. A warning that fires on its own correct work is worse
than no warning.

STATUS PAGE + WIDGET. The Homepage uptimekuma widget reads a PUBLISHED status
page (/api/status-page/<slug>), not the admin API -- which is why the widget
labels were deliberately absent from the rebuild: a dashboard widget pointed at
a 404 is the suspected mechanism behind both of Homepage's unkillable D-state
wedges, so shipping one on purpose would have been daft.

The page now exists at slug `nethealth` (the pre-rebuild slug, so old references
still resolve) and is DECLARED IN monitors.yaml, applied by `kuma seed`. Same
principle as the notification channel: a from-scratch rebuild restores the page,
the channel and the monitors together, and nothing the widget depends on lives
only in Kuma's database. Verified in a browser: "13 SITES UP / 0 SITES DOWN /
100% UPTIME" on the dashboard.

⚠ saveStatusPage calls imgDataUrl.startsWith() unconditionally, so passing null
throws and leaves the page CREATED BUT EMPTY -- which reads as success from
/api/status-page (200, correct title) while the group list is silently blank.
Pass "" instead. Commented at the call site.
2026-09-22 00:55:29 -07:00

93 lines
4.5 KiB
YAML

# uptime-kuma — the fleet's SERVICE-layer monitor.
#
# REBUILT FROM SCRATCH 2026-09-21 (operator-authorised: "uptime-kuma was never
# really used... you can even dump the existing container and config and start
# over from scratch"). The previous instance carried four monitors, all of them
# firewall pings, and 21-month-old code. Nothing was migrated.
#
# ⚠️ IMAGE TAG — DO NOT USE `:latest` HERE. Upstream keeps `latest` pointing at
# the 1.x line, so a pull in August 2026 handed us an image BUILT 2024-12-20
# (1.23.16). Measured, not assumed: `latest` and `1` resolve to the same
# digest, while `2`/`next` carry 2.5.5. All development is on 2.x, which has
# been stable since 2.2.0 (2026-03-05) across twelve releases with zero
# prereleases. Pinned EXACTLY rather than floating on `2` for the same reason
# `latest` burned us: a floating tag is a version you did not choose.
#
# MOVED esh-docker-vm -> ana-docker in the same rebuild, for two reasons:
# 1. House placement rule (CLAUDE.md): "Cross-site services (e.g. Beszel hub,
# Dozzle hub) live on ana-docker and pull from agents on the other hosts."
# A fleet-wide service monitor is exactly that, and it now sits beside the
# Beszel hub it complements.
# 2. esh-docker-vm has wedged unkillably TWICE in four months (2026-06-03 and
# 2026-09-18, both homepage in D-state). The fleet's service monitor should
# not live on the least reliable box in the fleet — and a monitor cannot
# report the failure of the host it runs on. Beszel covers the host layer;
# this covers the service layer; they should not share a failure domain.
#
# THE LANE SPLIT this service exists to fill (measured 2026-09-21):
# Beszel -> 18 hosts x {Status, CPU, Memory, Disk, Temperature}. Its alerts
# bind to a SYSTEM with a threshold; there is no URL column, so it
# is structurally incapable of "this endpoint should return 200".
# Kuma -> is the service actually serving. This file.
# Homepage-> DISPLAY ONLY. It polls 38 URLs and alerts nobody; it is a
# dashboard, never a monitor.
# The gap between the first two is where homepage sat dead for three days while
# both tools reported correctly.
name: uptimekuma
services:
uptime-kuma:
image: louislam/uptime-kuma:2.5.5
container_name: uptime-kuma
# Normalised from the adopted-as-run `always` — the 2026-08-18 README said
# this was "worth normalising on the next deliberate touch", and a rebuild
# from scratch is that touch. `always` revives a container that was stopped
# ON PURPOSE, which is the wrong behaviour for a service we may deliberately
# take down during maintenance.
restart: unless-stopped
environment:
# Skips 2.x's interactive "choose a database" first-run screen.
# setup-database.js: UPTIME_KUMA_DB_TYPE overrides db-config.json and
# writes it, so the stack comes up ready for the admin-account step
# instead of parking on a form. SQLite is right here -- one operator,
# a few dozen monitors; MariaDB is for the multi-thousand-check case.
UPTIME_KUMA_DB_TYPE: sqlite
ports:
- "3001:3001"
volumes:
- uptime-kuma:/app/data
labels:
# Monitoring, NOT Apps — this is the group the dashboard's layout reserves
# for Backrest / Beszel / Dozzle / this.
- homepage.group=Monitoring
- homepage.name=Uptime Kuma
- homepage.icon=mdi-arrow-up-bold-circle
- homepage.description=Service monitoring (fleet)
- homepage.href=http://10.250.50.70:3001
- homepage.siteMonitor=http://10.250.50.70:3001
# The widget reads a PUBLISHED status page, not the admin API:
# /api/status-page/<slug> and /api/status-page/heartbeat/<slug>. These
# labels were deliberately absent from the 2026-09-21 rebuild until that
# page existed, because a dashboard widget pointed at a 404 is the
# suspected mechanism behind BOTH of Homepage's unkillable D-state wedges
# (incident_esh_docker_nfs_boot_race, 2026-06-03) -- shipping one on
# purpose would have been daft.
#
# The page is defined in monitors.yaml (`status_page:`) and applied by
# `scripts/kuma seed`, so a from-scratch rebuild restores the slug this
# points at. Slug `nethealth` is the one the pre-rebuild instance used.
- homepage.widget.type=uptimekuma
- homepage.widget.url=http://10.250.50.70:3001
- homepage.widget.slug=nethealth
networks:
- tnet
volumes:
uptime-kuma: {}
networks:
tnet:
name: traefik-net
external: true