feat(observability): env-driven Slack/Discord/webhook Alertmanager receivers - #48
Merged
Conversation
…ceivers
The default observability stack already shipped 14 Prometheus alert
rules (API 5xx + latency, Postgres health, disk/memory/CPU pressure,
Traefik down), but Alertmanager's `default` receiver was an empty
stub — so alerts fired into the Alertmanager UI and nowhere else.
This wires real notification delivery for the three formats people
actually ask for.
Approach
--------
Alertmanager has no native env-var substitution in its config, so a
small `entrypoint.sh` renders alertmanager.yml from `ALERTMANAGER_*`
env vars at container boot, then execs the real Alertmanager binary.
The receiver block is built conditionally:
• ALERTMANAGER_SLACK_WEBHOOK_URL set → `slack_configs` block emitted
with a readable Slack message template (severity + component +
summary + description). Same env handles Discord because Discord
accepts Slack-format payloads at the `/slack` suffix — paste a
Discord webhook URL with `/slack` appended and it just works.
• ALERTMANAGER_WEBHOOK_URL set → `webhook_configs` block emitted
for operators who want Alertmanager's native JSON (custom bridges,
n8n, PagerDuty events-v2 translators, etc.).
• Both can be set; alerts fan out to both.
• Neither set → receiver has no notifiers; alerts surface in the
Alertmanager UI at :9093 only. Still a valid config — verified
with `amtool check-config`.
Routing already supports `severity=page` (1h re-notify, urgent) and
`severity=warn` (12h re-notify, no pager-spam) via the existing rule
labels — the new alertmanager.yml header preserves that.
Files
-----
- compose/alertmanager/entrypoint.sh (new) — renders + execs.
- compose/alertmanager/alertmanager.yml (deleted) — static file
superseded by the env-driven render.
- compose/docker-compose.observability.yml — alertmanager service now
uses entrypoint, declares ALERTMANAGER_* env vars, mounts the
script in place of the static config.
- compose/.env.example — documents the new env vars + the Discord
/slack-suffix trick.
- infra/compose/docs/alerts.md (new) — full walkthrough: which rules
ship, Slack setup, Discord setup, generic webhook setup, tuning
thresholds, adding rules, end-to-end ping test.
- infra/compose/docs/observability.md — old "Adding alert rules"
paragraph replaced with an Alerts section pointing at alerts.md.
Verification
------------
- `STACK=dev ./dev.sh config --quiet` exits 0 with the new compose.
- `shellcheck -x -S warning` clean on entrypoint.sh.
- amtool check-config passes against the rendered config in both the
both-URLs case (slack_configs + webhook_configs both emitted) and
the no-URLs case (bare `- name: default` receiver, alerts UI-only).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
agjs
enabled auto-merge (squash)
May 29, 2026 09:55
agjs
added a commit
that referenced
this pull request
May 29, 2026
Catches the operator-facing docs site up with all the infra and correlation work that landed across PRs #45, #46, #47, and #48 — defaults flip + dashboards + structured logging + user/trace correlation + env-driven alert receivers. topics/observability.mdx - "What ships" — five dashboards (was one); Pino pipeline detail; Alertmanager wired with env-driven receivers (was "you wire it yourself"). - "Default dashboards" — full per-dashboard breakdown (API, API logs, UI logs, Postgres, Host) with what each is best at and a pointer to the click-through data links on trace_id / userId / requestId. - "Design choices" — added "on by default", trace_id flow, and env-driven Alertmanager entries; dropped the now-misleading "Prometheus + Loki, not OpenTelemetry" claim (the Sentry tracing side does carry traces). - "Querying" — LogQL examples now use the `level` label (was the broken `level=ERROR` substring match) and include trace_id / userId pivot queries. - "Adding an alert" — points at the single bundled rules.yml and the new Alerts topic. topics/error-tracking.mdx - New "Correlation: GlitchTip ↔ Loki ↔ requestId" section with a mermaid diagram of the trace_id flow (browser → API → Loki + GlitchTip), a description of what flows automatically (trace_id / span_id / userId / requestId), and the three click-through paths (Grafana → GlitchTip via dashboard data links, Grafana → Grafana via the requestId link, GlitchTip → Grafana via the operator-side one-time tag link template). - Design-choices block updated with on-by-default GlitchTip and the user.id / trace_id tagging. topics/alerts.mdx (new) - New topic page promoting the alerts walkthrough to the user-facing docs site. Covers what the 14 default rules fire on, the three receiver formats (Slack, Discord-via-/slack-suffix, generic webhook), an end-to-end curl one-liner to ping a fake alert, how to add rules, and the cost-of-pager-fatigue note. reference/env-vars.mdx - New entries for ALERTMANAGER_SLACK_WEBHOOK_URL, ALERTMANAGER_SLACK_CHANNEL, ALERTMANAGER_WEBHOOK_URL. reference/commands.mdx - "Hot-reload Prometheus rules" — `curl POST /-/reload`. - "Ping a fake alert (verify receiver wiring)" — the verification one-liner from the new Alerts topic. astro.config.mjs - Wire the new Alerts topic into the Topics sidebar between Observability and Provisioning with OpenTofu. Verification: `bun run build` clean, 66 pages built (was 65, now +1 for Alerts), pagefind index built without errors. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The default observability stack already shipped 14 Prometheus alert
rules (API 5xx + latency, Postgres health, disk/memory/CPU pressure,
Traefik down), but Alertmanager's
defaultreceiver was an emptystub — so alerts fired into the Alertmanager UI and nowhere else.
This wires real notification delivery for the three formats people
actually ask for.
Approach
Alertmanager has no native env-var substitution in its config, so a
small
entrypoint.shrenders alertmanager.yml fromALERTMANAGER_*env vars at container boot, then execs the real Alertmanager binary.
The receiver block is built conditionally:
• ALERTMANAGER_SLACK_WEBHOOK_URL set →
slack_configsblock emittedwith a readable Slack message template (severity + component +
summary + description). Same env handles Discord because Discord
accepts Slack-format payloads at the
/slacksuffix — paste aDiscord webhook URL with
/slackappended and it just works.• ALERTMANAGER_WEBHOOK_URL set →
webhook_configsblock emittedfor operators who want Alertmanager's native JSON (custom bridges,
n8n, PagerDuty events-v2 translators, etc.).
• Both can be set; alerts fan out to both.
• Neither set → receiver has no notifiers; alerts surface in the
Alertmanager UI at :9093 only. Still a valid config — verified
with
amtool check-config.Routing already supports
severity=page(1h re-notify, urgent) andseverity=warn(12h re-notify, no pager-spam) via the existing rulelabels — the new alertmanager.yml header preserves that.
Files
superseded by the env-driven render.
uses entrypoint, declares ALERTMANAGER_* env vars, mounts the
script in place of the static config.
/slack-suffix trick.
ship, Slack setup, Discord setup, generic webhook setup, tuning
thresholds, adding rules, end-to-end ping test.
paragraph replaced with an Alerts section pointing at alerts.md.
Verification
STACK=dev ./dev.sh config --quietexits 0 with the new compose.shellcheck -x -S warningclean on entrypoint.sh.both-URLs case (slack_configs + webhook_configs both emitted) and
the no-URLs case (bare
- name: defaultreceiver, alerts UI-only).Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com