Files
esh-pfi-infrastructure/stacks/homepage
vh 42c594c29f fix(searxng,seafile): repair wget healthcheck argv, restore seafile after 3-month outage
searxng: the healthcheck passed '--tries' and '--spider' as separate argv
entries, so wget consumed '--spider' as the value of '--tries'. Spider mode
never engaged and every 30s probe downloaded the response to disk; the
container's working directory had accumulated 295,287 healthz.N files since
April, and the directory scan to pick the next free filename is what
intermittently blew the 10s timeout and flapped the dashboard card to
UNHEALTHY. Restored '--tries=1'. The junk was in the writable layer, so the
recreate cleared it. Now healthy, fails=0, 200 in 0.16s.

seafile: none of the three services declared a restart policy, so Docker
defaulted them to 'no'. The daemon stopped all three within 200ms on
2026-05-06 and nothing brought them back — a three-month outage whose only
trace was an EXITED card. Exit 255 is what a container ignoring SIGTERM
reports when the daemon stops it, not a crash. Added restart: unless-stopped.
Stack is back up; mysql gates on its healthcheck as designed and seahub
started without the race. 302 -> login page.

Both stacks were running unmanaged on ana-docker and are now tracked here.

homepage: AI tab reordered by clickability per operator — chat frontends,
ComfyUI and the control plane on top; vLLM /docs seats and TTS endpoints
below. Corrects the previous commit's UNRESOLVED tab-bar section: it was
warm-up time after a recreate, not a defect.
2026-08-18 22:27:15 -07:00
..

homepage — the fleet dashboard

ghcr.io/gethomepage/homepage on esh-docker-vm (10.0.50.45:5100), behind Traefik as eshhome / eshhome.esteban.net. Config is plain YAML — no database, no UI-written state — which is why it belongs in this repo like any other stack.

Brought under version control 2026-08-17. Before that it was edited in place on the host, and had accumulated six hand-rolled services.yaml.bak-* files as its only version history. Those were removed; git is the history now. Edit here, then scripts/deploy-stack.sh esh-docker-vm homepage.

How services get on the dashboard

Two paths, and mixing them is the classic failure:

  1. Docker label auto-discovery — the default. A stack carries homepage.group= / homepage.name= / homepage.icon= / homepage.description= / homepage.href= labels and appears automatically. conf/docker.yaml wires five engines over plaintext :2375: esh-docker-vm, ana-docker, nh3-docker, ana-ml2, irv-ml1.
  2. Manual entries in conf/services.yaml — for anything that is not a labelled container on one of those five hosts: hardware, BMCs, hypervisors, printers, and user-level systemd services (The Booth, Voice Design Studio).

Never list a labelled container manually — it renders twice. The comments in services.yaml mark which groups are auto-populated (AI ×7, Service Networking, Monitoring). Respect them.

Labels only apply on container recreate. Changing homepage.group= on a compose file and running restart does nothing; the container must be recreated.

Layout

conf/settings.yaml owns tabs, group order, and column counts — services.yaml owns what exists, settings.yaml owns where it sits. Four tabs: Main, AI, Infrastructure, Toolchain. A group listed in layout: with no members simply renders empty, so a group can look "dead" when its provider host is unreachable rather than when the group is wrong.

Foot-guns found in the 2026-08-17 audit

  • HOMEPAGE_ALLOWED_HOSTS matches host and port. The entry 10.0.50.45 does not cover http://10.0.50.45:5100/ — that combination was being rejected with Host validation failed in the container log while the Traefik hostnames worked fine. Fixed 2026-08-17; every host:port the dashboard is reached by needs its own entry. See .env.example.
  • The .env was mode 644 with the Plex and Jellyfin API keys in it. Now 600. It is root-owned, so it needs the infra-ops identity to edit — lkraven on this host has password-sudo only.
  • Homepage renders client-side. Grepping the served HTML to verify a config change gives false readings — first a stale prerender, then an empty page. GET /api/services is the honest check; it returns the resolved group/service tree. Config edits also need a container recreate, not a restart: a restart keeps the writable layer and its cached render.
  • :2375 is plaintext and unauthenticated on all five engines. Fine on a trusted LAN, and unchanged by this commit, but it is real exposure: anything that can reach those ports has full Docker control of that host. docker.yaml carries a commented TLS example for when that stops being acceptable.
  • ping: cards can only be judged from esh-docker-vm. Probing them from another box gives false FAILs — ICMP is filtered across some site links. All 34 entries were verified reachable from the dashboard host on 2026-08-17.

2026-08-18 cleanup

Three fixes, all in this stack's config except where noted:

  • UltraSeedbox appeared on all four tabs. The bookmark group had no entry in settings.yaml's layout: block at all, and Homepage's documented behaviour is that "if a group has no tab specified (and tabs are set on other groups), services and bookmarks will be shown on all tabs." It now carries tab: Main plus style: row / columns: 4, which also turns eight full-width bars into a compact grid. Any group added without a tab: will do this again — the rule is now written at the top of the layout block.
  • Uptime Kuma rendered twice. It was listed manually under Monitoring in services.yaml and labelled homepage.group=Apps on its container. The manual block is gone; the container's label now says Monitoring and carries homepage.siteMonitor. The container was adopted into this repo at stacks/uptimekuma/ in the same commit — it had been running unmanaged.
  • Column counts were fiction. Several groups declared more columns than they had members, so the last row of each was mostly dead space (Notes: 1 card in a 4-wide row). Columns now track member counts; see the rule in settings.yaml. Check with GET /api/services, which prints live per-group counts.

RESOLVED — the tab bar takes several minutes to appear after a recreate

Status: closed, and the answer is "wait". After the 2026-08-18 recreate the client render came up with no tab bar, no wallpaper and no i18n (the search box read the raw key search.search); groups fell back to side-by-side columns. It restored itself with no further intervention. A freshly recreated homepage container needs a few minutes before the client render is whole — far longer than the healthcheck takes to report healthy, which is the trap: docker ps says the service is up while the page is still visibly wrong.

So: after any docker compose up -d --force-recreate here, do not judge the dashboard for at least ~5 minutes, and do not start changing config to chase it. Everything below is the evidence trail from doing exactly that, kept because it rules out four plausible causes and will save the next session the same hour.

What it is not — both obvious suspects were tested and cleared:

  • Not the config changes above. Restoring settings.yaml and services.yaml to their committed pre-cleanup versions reproduces the breakage exactly. So does the pre-adoption backup config in /opt/docker-bu/conf/homepage/.
  • Not the v2.0.0 release. A throwaway container on v1.13.2 (the last v1, 2026-06-09) against the same config shows identical symptoms. The image never changed anyway: the working container and the broken one both report v2.0.0 / rev 17456f2, and only one homepage image exists on the host.

What is known. The server-rendered HTML still contains the tab markup, the wallhaven background URL and useEqualHeights — so settings.yaml is being read and delivered correctly. The loss happens client-side, with no page error, no failed chunk and no non-200 beyond two unrelated Uptime Kuma widget 403s. GET /api/validate returns []. A fresh container never renders tabs here regardless of image version, config version, PUID/PGID, or whether Docker discovery is mounted at all.

What it actually was: warm-up time. Every throwaway container in the list above was judged within ~30 seconds of starting, which is why they all looked broken — they were all in the same warm-up window, and that consistency read as a reproduction when it was really the same mistake five times. The live container recovered on its own once left alone. The lesson is a measurement discipline, not a config one, and it sits alongside the existing warning that docker ps health and a correct render are different questions.

Before/after evidence: ~/booth-data/homepage-cleanup/ on nh3-dev → http://10.100.10.50:8090/b/homepage-cleanup/ (24h TTL).

Open question

ESH-FileBot (10.0.50.70) is still described as "role TBC" — it responds to ping, but nobody has written down what it does. Worth resolving or removing.