| 1 | // Managed by Ansible. River config for Grafana Alloy. |
| 2 | // |
| 3 | // Two scrape jobs: |
| 4 | // - node_exporter (host: CPU, mem, disk, network, fs, etc.) |
| 5 | // - shithubd /metrics (app: req latency, DB pool, panics, jobs) |
| 6 | // |
| 7 | // Both push to Grafana Cloud Mimir via the prometheus.remote_write |
| 8 | // component below. The basic-auth username/password come from |
| 9 | // /etc/alloy/credentials.env via EnvironmentFile= on the unit. |
| 10 | // |
| 11 | // Why this shape (not separate prometheus + grafana on the droplet): |
| 12 | // - One binary, one config, one systemd unit. |
| 13 | // - No inbound 9090 to firewall — push-only. |
| 14 | // - Free-tier Grafana Cloud handles ~10k active series, way over |
| 15 | // what one shithubd droplet emits. |
| 16 | |
| 17 | prometheus.remote_write "grafana_cloud" { |
| 18 | endpoint { |
| 19 | url = sys.env("GRAFANA_CLOUD_PROM_URL") |
| 20 | basic_auth { |
| 21 | username = sys.env("GRAFANA_CLOUD_PROM_USER") |
| 22 | password = sys.env("GRAFANA_CLOUD_PROM_TOKEN") |
| 23 | } |
| 24 | } |
| 25 | // Drop the most cardinality-expensive labels from go runtime metrics |
| 26 | // before remote_write. The free tier limits unique series; bare go_* |
| 27 | // gives plenty of signal without the per-bucket explosions. |
| 28 | external_labels = { |
| 29 | instance = "{{ ansible_hostname }}", |
| 30 | cluster = "shithub-prod", |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | // ── node_exporter ─────────────────────────────────────────────── |
| 35 | prometheus.scrape "node_exporter" { |
| 36 | targets = [ |
| 37 | { __address__ = "127.0.0.1:9100", job = "node" }, |
| 38 | ] |
| 39 | forward_to = [prometheus.remote_write.grafana_cloud.receiver] |
| 40 | scrape_interval = "30s" |
| 41 | scrape_timeout = "10s" |
| 42 | } |
| 43 | |
| 44 | // ── shithubd /metrics ─────────────────────────────────────────── |
| 45 | // enable_compression = false: shithubd's HTTP middleware auto-gzips |
| 46 | // when the client advertises Accept-Encoding: gzip and correctly sets |
| 47 | // Content-Encoding: gzip on the response. Alloy 1.16's Prometheus |
| 48 | // scraper mis-handles that path: it parses the raw 0x1f gzip magic |
| 49 | // byte as text and the scrape fails with up=0. Disabling Accept- |
| 50 | // Encoding negotiation forces shithubd to return plain text. The |
| 51 | // /metrics payload is small enough that wire-size cost is irrelevant. |
| 52 | // Revisit once Alloy fixes the gzip-aware text parser upstream. |
| 53 | prometheus.scrape "shithubd" { |
| 54 | targets = [ |
| 55 | { __address__ = "127.0.0.1:8080", job = "shithubd", __metrics_path__ = "/metrics" }, |
| 56 | ] |
| 57 | forward_to = [prometheus.remote_write.grafana_cloud.receiver] |
| 58 | scrape_interval = "15s" |
| 59 | scrape_timeout = "10s" |
| 60 | enable_compression = false |
| 61 | } |