Files
Frank b96359e628 fix(observability): give loki an exec-form healthcheck in its image
The loki unit came up 'unhealthy' after the quadlet cutover. Cause: a unit's
HealthCmd reaches podman as CMD-SHELL, i.e. /bin/sh -c, and the loki image is
distroless — no shell exists in it. The busybox command itself works (probed
by hand: /ready answers 'ready'); it was the shell wrapper that could not run.
podman does not accept an exec-form command through --health-cmd either: it
stores a JSON array as one literal argument.

The command therefore moves into dankclaw/loki/Containerfile as a
HEALTHCHECK ... CMD [...] exec form, which is what that image exists for — it
already ships the static busybox solely so a healthcheck has something to run.
podman reads the image's healthcheck when the unit declares none.
2026-09-11 16:01:21 -07:00

58 lines
2.2 KiB
INI

# Quadlet unit: loki (rootless, boot-persistent).
#
# Install path on dankclaw: ~/.config/containers/systemd/loki.container
# Unit name: loki.service (container name: loki)
#
# Log store. This replaces the compose service of the same name in
# dankclaw/compose.yaml ("Observability" block).
#
# IMAGE. franksdank/loki is built locally by deploy-remote.sh
# (build_compose_image) from dankclaw/loki/Containerfile: upstream
# grafana/loki plus a static busybox, because loki is distroless and the
# healthcheck has nothing to run inside it. Pull=never keeps a boot from
# trying to fetch that local tag from a registry. The localhost/ prefix is
# required — this host's quadlet path cannot resolve a registry-prefixed ref
# from local storage at create time.
#
# PUBLISHING. Bound to 127.0.0.1: Alloy pushes to loki:3100 over
# dankclaw_default, Caddy proxies the guarded vhost to loki:3100 the same way,
# and the homepage status timer probes 127.0.0.1:3100. Loki has
# auth_enabled: false, so the compose service's 0.0.0.0 binding was an
# unauthenticated log-ingest and read API on every host interface.
#
# CONFIG. /opt/loki/config/loki-config.yaml is installed from
# shared/loki/loki-config.yaml on every deploy (single-binary, filesystem
# storage, 30-day retention). Chunks and rules live in /opt/loki/data.
#
# Rootless: container root maps to the invoking user (dank), who owns
# /opt/loki/data; the image's default user (10001) maps to a subuid that
# cannot write it.
[Unit]
Description=Loki log store (Dankclaw)
After=network-online.target
Wants=network-online.target
[Container]
ContainerName=loki
Image=localhost/franksdank/loki:latest
Pull=never
Network=dankclaw_default
User=0:0
Exec=-config.file=/etc/loki/loki-config.yaml
PublishPort=127.0.0.1:3100:3100
Volume=/opt/loki/config:/etc/loki
Volume=/opt/loki/data:/loki
# No HealthCmd here on purpose. A unit's HealthCmd reaches podman as
# CMD-SHELL, which means `/bin/sh -c <command>` — and this image is
# distroless, with no shell at all. The healthcheck therefore ships in the
# image (dankclaw/loki/Containerfile) in exec form, where it also carries its
# interval, timeout, start period and retry count.
[Service]
Restart=always
RestartSec=10
[Install]
WantedBy=default.target