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.
58 lines
2.2 KiB
INI
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
|