thermograph/observability/alloy/docker-compose.agent.yml
emi d138f00a20
Some checks failed
Sync infra to hosts / sync-beta (push) Has been skipped
Sync infra to hosts / sync-prod (push) Has been skipped
Sync infra to hosts / sync-dev (push) Failing after 6s
secrets-guard / encrypted (push) Successful in 6s
shell-lint / shellcheck (push) Successful in 13s
Validate observability stack / validate (push) Successful in 17s
PR build (required check) / changes (pull_request) Successful in 6s
secrets-guard / encrypted (pull_request) Successful in 5s
PR build (required check) / build-backend (pull_request) Has been skipped
shell-lint / shellcheck (pull_request) Successful in 6s
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Successful in 18s
PR build (required check) / gate (pull_request) Successful in 2s
infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103)
2026-07-26 06:56:38 +00:00

71 lines
3.5 KiB
YAML

# The Alloy log-collector agent. Runs on EACH node (vps1, vps2) — one per node
# — reading that node's Docker containers, Caddy logs, and the app's JSON logs,
# and shipping them to the central Loki on vps1 over the mesh.
#
# Per-node config comes from the environment (an .env file next to this, or
# exported before `up`):
# ALLOY_NODE the MACHINE: vps1 | vps2 | desktop
# ALLOY_ENV the default ENVIRONMENT label for logs on this node that
# aren't attributable to a specific stack (Forgejo, Grafana,
# the portfolio site): vps1 -> dev, vps2 -> prod
# APPLOGS_VOLUME this node's primary applogs volume
# LOKI_URL push endpoint. All nodes push over the mesh:
# http://10.10.0.2:3100/loki/api/v1/push
#
# Deploy on vps1 (dev + Forgejo + the monitoring stack itself):
# ALLOY_NODE=vps1 ALLOY_ENV=dev APPLOGS_VOLUME=thermograph-dev_applogs \
# LOKI_URL=http://10.10.0.2:3100/loki/api/v1/push \
# docker compose -f docker-compose.agent.yml up -d
#
# Deploy on vps2 (prod + beta) — note the SECOND file, which adds beta's log
# volume:
# ALLOY_NODE=vps2 ALLOY_ENV=prod APPLOGS_VOLUME=thermograph_applogs \
# BETA_APPLOGS_VOLUME=thermograph-beta_applogs \
# LOKI_URL=http://10.10.0.2:3100/loki/api/v1/push \
# docker compose -f docker-compose.agent.yml -f docker-compose.agent.beta.yml up -d
#
# WHY THE VOLUME NAME IS A VARIABLE NOW, AND WHY BETA IS AN OVERLAY. The name
# used to be the literal `thermograph_applogs`, with a comment telling you to
# hand-edit two lines on the dev node. That was survivable while one node ran
# one environment. vps2 runs two, each with its own applogs volume, and a single
# hardcoded mount there would ship one environment's structured logs and
# silently drop the other's.
#
# Beta's mount is a separate overlay file rather than a second variable in this
# one because the second mount is labelled host="beta" unconditionally. Pointing
# it at dev's volume on vps1 just to satisfy compose would ship every dev log
# line a second time labelled as beta — inventing an environment's worth of
# fake data on a box beta does not run on.
services:
alloy:
image: grafana/alloy:v1.9.1
command:
- run
- /etc/alloy/config.alloy
- --storage.path=/var/lib/alloy/data
- --server.http.listen-addr=0.0.0.0:12345
environment:
ALLOY_NODE: ${ALLOY_NODE:?set ALLOY_NODE (vps1|vps2|desktop) — the MACHINE}
ALLOY_ENV: ${ALLOY_ENV:?set ALLOY_ENV (dev|beta|prod) — this node's default environment label}
LOKI_URL: ${LOKI_URL:?set LOKI_URL}
volumes:
- ./config.alloy:/etc/alloy/config.alloy:ro
# Read-only Docker socket: discover + tail every container's stdout/stderr.
- /var/run/docker.sock:/var/run/docker.sock:ro
# Caddy's host access logs (runs on the host, not a container).
- /var/log/caddy:/var/log/caddy:ro
# The app's structured JSON logs, straight off its named volume.
- applogs:/applogs:ro
- alloy_data:/var/lib/alloy/data
restart: unless-stopped
volumes:
# The app's existing log volume (created by the app's own stack). external:true
# means compose references it, never creates or deletes it. The NAME varies by
# node — see the header — while the mount path stays /applogs, which is what
# lets one config.alloy serve every node.
applogs:
external: true
name: ${APPLOGS_VOLUME:?set APPLOGS_VOLUME (e.g. thermograph_applogs on vps2, thermograph-dev_applogs on vps1)}
alloy_data: {}