homelab-codex-ws/services/fleet-prometheus/docker-compose.yml
oskar 4518b15f98 feat(fleet-prometheus): scaffold fleet liveness Prometheus (VPS)
Clean scaffold of a Prometheus instance dedicated to fleet liveness, deliberately
separate from the home `prom` on PIHA. Scrapes only itself + the VPS-local
node_exporter for now.

- image pinned to prom/prometheus:v3.5.0 (LTS) — no :latest anti-pattern
- TSDB retention 15d / 2GB on the tight VPS; --web.enable-lifecycle for reloads
- mem_limit 512m, oom_score_adj +200 (sacrificial OOM victim before control-plane)
- tailscale-internal exposure via plain published 9090, mirroring control-plane
- node_exporter scraped via host.docker.internal:9100 (it runs network_mode: host)
- secret-free prometheus.yml; alerting/rule_files left as commented placeholders
- in-container healthcheck via busybox wget (present in the image; curl is not)

Smoke: docker compose config OK; promtool check config SUCCESS; up -d ->
/-/healthy + /-/ready OK; both targets (prometheus, fleet-node) report up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 18:07:18 +02:00

51 lines
2.3 KiB
YAML

services:
fleet-prometheus:
# Pinned to the Prometheus 3.5 LTS line — NEVER :latest. The home `prom`
# on PIHA runs :latest, which is the anti-pattern we are deliberately not
# repeating here: a fleet liveness source must be reproducible.
image: prom/prometheus:v3.5.0
container_name: fleet-prometheus
restart: unless-stopped
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
# Tight VPS (3.7 GiB RAM + 4 GiB swap): keep the TSDB small. Whichever
# of time/size is hit first triggers compaction/deletion.
- '--storage.tsdb.retention.time=15d'
- '--storage.tsdb.retention.size=2GB'
# Allow `POST /-/reload` so liveness rules (added in a later step) can be
# hot-loaded without restarting and losing the scrape gap.
- '--web.enable-lifecycle'
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- fleet_prometheus_tsdb:/prometheus
ports:
# tailscale-internal: exposed exactly like control-plane (plain published
# HOST:CONTAINER mapping, no public npm reverse-proxy entry). Reachability
# is constrained to the Tailscale mesh at the VPS firewall layer, same as
# control-plane's 18180. Do NOT add this to npm / public DNS.
- "9090:9090"
# node_exporter on the VPS runs with network_mode: host, so it listens on
# the host's :9100. This bridged container reaches it via host-gateway.
extra_hosts:
- "host.docker.internal:host-gateway"
# Memory: VPS has 4 GiB RAM. 512m cgroup ceiling; Docker restarts the
# container on breach. oom_score_adj is POSITIVE on purpose — fleet
# monitoring is less critical than the control-plane (-900), so the host
# OOM-killer should sacrifice this container FIRST, never the control-plane.
mem_limit: 512m
oom_score_adj: 200
# The prom/prometheus image is busybox-based and ships `wget` (but no curl),
# so an in-container HTTP check works here — unlike the vikunja image, which
# had neither and broke its healthcheck permanently.
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9090/-/healthy"]
interval: 30s
timeout: 10s
retries: 5
start_period: 30s
volumes:
fleet_prometheus_tsdb:
name: fleet-prometheus_tsdb