infra/Read
/CLAUDE.md(project root) anddocs/DOCKER.mdbefore editing anything underinfra/. This file is the area-specific addendum: the rules that get violated most often when modifying compose files, observability configs, or runbooks.
Every long-running service in every compose file must carry the project's logging cap. Compose files in this tree define a YAML anchor near the top:
x-logging: &briven-logging
driver: json-file
options:
max-size: '10m'
max-file: '3'
Every service that has restart: unless-stopped (or restart: always) must carry:
restart: unless-stopped
logging: *briven-logging
If restart: unless-stopped is present without logging: *briven-logging immediately below it, the change is incomplete. This is non-negotiable per docs/DOCKER.md §7; without the cap, a single noisy container can fill the kvm4 host disk and take every project down with it.
One-shot containers (restart: 'no', like minio-init) don't need the cap — they exit before accumulating logs. Everything else does.
infra/These come straight from docs/DOCKER.md. They're listed here so the rule fires at the level you're working at, not three layers up.
docker_sd_configs. Promtail discovers via __path__: /var/lib/docker/containers/*/*-json.log with static_configs. See infra/observability/promtail/config.yaml for the working pattern.unix:///var/run/docker.sock host:` line. That's the daemon. The daemon is shared./var/run/docker.sock bind-mount. The Promtail container only needs /var/lib/docker/containers:/var/lib/docker/containers:ro.docker_sd_configs in Prometheus scrape configs.unix:///var/run/docker.sock. The Docker daemon's /metrics endpoint is the daemon — don't.static_configs with service-name DNS (e.g. briven-api:3001) or, for host metrics, cAdvisor + node_exporter (which read cgroupfs and /proc, not the daemon).restart: always on a container whose job is to poll the registry.docker compose pull && up -d.docker logs -f in a long-running shell. Use tail -F /var/lib/docker/containers/<id>/<id>-json.log or query Loki.docker ps / docker stats / docker inspect. Use cAdvisor, cgroupfs, /proc, or the container's own /metrics endpoint.| File | Purpose |
|---|---|
dokploy/compose.yml |
Standalone Docker / Coolify / plain compose |
dokploy/compose.dokploy.yml |
Dokploy-managed (no container_name; uses external dokploy-network) |
coolify/compose.yml |
Coolify-managed (no traefik labels — coolify owns routing) |
observability/compose.yml |
Grafana + Loki + Prometheus + Promtail (separate Dokploy project) |
All four are checked by python3 -c "import yaml; ..." for syntax + cap coverage in the pre-merge audit; rerun it whenever you add or remove a service:
python3 -c "
import yaml
for f in ['infra/dokploy/compose.yml','infra/dokploy/compose.dokploy.yml','infra/coolify/compose.yml','infra/observability/compose.yml']:
with open(f) as fh: d=yaml.safe_load(fh)
missing=[s for s,c in d['services'].items() if c.get('restart')=='unless-stopped' and 'logging' not in c]
print(f, '— missing log cap:', missing or 'none')
"
docker_sd_configs anywhere in the change/var/run/docker.sock bind-mountdocker logs -f / docker stats / docker inspect polling loop in any script the operator runs on the hostlogging: *briven-loggingstatic_configs or cAdvisor / node_exporterAnything that fails the checklist is a blocking issue, not a nit. The shared-daemon class of bug surfaces as host-wide slowness across unrelated projects; revert is the right response if the violation made it to production.