2026-07-26 06:56:38 +00:00
|
|
|
# Loki — the single log store for the whole Thermograph fleet: prod and beta
|
|
|
|
|
# (both on vps2, as separate Swarm stacks) and dev (vps1, mesh-only, its own
|
|
|
|
|
# compose project). Every node's Alloy agent pushes here over the WireGuard
|
|
|
|
|
# mesh; Grafana reads from here. Single-binary, filesystem-backed: no object
|
|
|
|
|
# storage, no clustering — right for a three-node hobby fleet, and everything
|
|
|
|
|
# lives on one volume you can back up or blow away.
|
Fleet log aggregation: Grafana + Loki + Alloy over the WireGuard mesh
A central Grafana + Loki stack (on beta) fed by a Grafana Alloy agent on
every node, replacing the old SSH-tailed single-host scripts/dashboard.py.
- docker-compose.yml: central Loki (mesh-only :3100) + Grafana (Caddy-fronted)
- loki/config.yml: single-binary, filesystem storage, 30-day retention
- alloy/config.alloy + docker-compose.agent.yml: per-node collector — every
container's stdout/stderr via the Docker socket, Caddy host logs, and the
app's structured JSON logs (errors/access/audit), each line tagged by node
- grafana/: auto-provisioned Loki datasource + a fleet-logs dashboard
(volume by service, error rate, upstream 429s, Caddy 5xx, notifier liveness,
live tail), with a per-node selector
- caddy-grafana.conf, README, .env.example
2026-07-21 16:11:52 +00:00
|
|
|
auth_enabled: false
|
|
|
|
|
|
|
|
|
|
server:
|
|
|
|
|
http_listen_port: 3100
|
|
|
|
|
grpc_listen_port: 9096
|
|
|
|
|
log_level: warn
|
|
|
|
|
|
|
|
|
|
common:
|
|
|
|
|
instance_addr: 127.0.0.1
|
|
|
|
|
path_prefix: /loki
|
|
|
|
|
storage:
|
|
|
|
|
filesystem:
|
|
|
|
|
chunks_directory: /loki/chunks
|
|
|
|
|
rules_directory: /loki/rules
|
|
|
|
|
replication_factor: 1
|
|
|
|
|
ring:
|
|
|
|
|
kvstore:
|
|
|
|
|
store: inmemory
|
|
|
|
|
|
|
|
|
|
schema_config:
|
|
|
|
|
configs:
|
|
|
|
|
- from: 2024-01-01
|
|
|
|
|
store: tsdb
|
|
|
|
|
object_store: filesystem
|
|
|
|
|
schema: v13
|
|
|
|
|
index:
|
|
|
|
|
prefix: index_
|
|
|
|
|
period: 24h
|
|
|
|
|
|
2026-07-24 04:37:41 +00:00
|
|
|
# 30 low-rate streams almost always hit chunk_idle_period (30m default) long before
|
|
|
|
|
# they'd ever fill chunk_target_size, so chunks were flushed 1.98% full on average
|
|
|
|
|
# (972 near-empty chunks for 30MB of actual log data). Give idle streams far longer
|
|
|
|
|
# to accumulate before an idle flush, and cap age/size so a chunk still can't grow
|
|
|
|
|
# unbounded.
|
|
|
|
|
ingester:
|
|
|
|
|
chunk_idle_period: 2h
|
|
|
|
|
max_chunk_age: 12h
|
|
|
|
|
chunk_target_size: 1572864
|
|
|
|
|
chunk_encoding: snappy
|
|
|
|
|
|
Fleet log aggregation: Grafana + Loki + Alloy over the WireGuard mesh
A central Grafana + Loki stack (on beta) fed by a Grafana Alloy agent on
every node, replacing the old SSH-tailed single-host scripts/dashboard.py.
- docker-compose.yml: central Loki (mesh-only :3100) + Grafana (Caddy-fronted)
- loki/config.yml: single-binary, filesystem storage, 30-day retention
- alloy/config.alloy + docker-compose.agent.yml: per-node collector — every
container's stdout/stderr via the Docker socket, Caddy host logs, and the
app's structured JSON logs (errors/access/audit), each line tagged by node
- grafana/: auto-provisioned Loki datasource + a fleet-logs dashboard
(volume by service, error rate, upstream 429s, Caddy 5xx, notifier liveness,
live tail), with a per-node selector
- caddy-grafana.conf, README, .env.example
2026-07-21 16:11:52 +00:00
|
|
|
limits_config:
|
|
|
|
|
# A hobby fleet's volume is tiny; keep 30 days and cap ingestion generously.
|
|
|
|
|
retention_period: 720h
|
|
|
|
|
reject_old_samples: true
|
|
|
|
|
reject_old_samples_max_age: 168h
|
|
|
|
|
max_query_series: 5000
|
|
|
|
|
allow_structured_metadata: true
|
|
|
|
|
volume_enabled: true
|
2026-07-24 04:37:41 +00:00
|
|
|
# No explicit ingestion/stream limits meant Loki fell back to its (much stricter)
|
|
|
|
|
# built-in defaults, which is why 25 pushes came back HTTP 429 with nothing in
|
|
|
|
|
# this file explaining why. These are sized for a three-node hobby fleet, not the
|
|
|
|
|
# multi-tenant defaults.
|
|
|
|
|
ingestion_rate_mb: 8
|
|
|
|
|
ingestion_burst_size_mb: 16
|
|
|
|
|
per_stream_rate_limit: 3MB
|
|
|
|
|
per_stream_rate_limit_burst: 10MB
|
|
|
|
|
max_global_streams_per_user: 1000
|
Fleet log aggregation: Grafana + Loki + Alloy over the WireGuard mesh
A central Grafana + Loki stack (on beta) fed by a Grafana Alloy agent on
every node, replacing the old SSH-tailed single-host scripts/dashboard.py.
- docker-compose.yml: central Loki (mesh-only :3100) + Grafana (Caddy-fronted)
- loki/config.yml: single-binary, filesystem storage, 30-day retention
- alloy/config.alloy + docker-compose.agent.yml: per-node collector — every
container's stdout/stderr via the Docker socket, Caddy host logs, and the
app's structured JSON logs (errors/access/audit), each line tagged by node
- grafana/: auto-provisioned Loki datasource + a fleet-logs dashboard
(volume by service, error rate, upstream 429s, Caddy 5xx, notifier liveness,
live tail), with a per-node selector
- caddy-grafana.conf, README, .env.example
2026-07-21 16:11:52 +00:00
|
|
|
|
|
|
|
|
compactor:
|
|
|
|
|
working_directory: /loki/compactor
|
|
|
|
|
retention_enabled: true
|
|
|
|
|
delete_request_store: filesystem
|
|
|
|
|
|
|
|
|
|
# No analytics phone-home from a private box.
|
|
|
|
|
analytics:
|
|
|
|
|
reporting_enabled: false
|