Signal Forge ADR-005: Separate collector configmaps per deployment mode
Status: Accepted
Decision: The Alloy collector configuration is split by mode:
- Cloud:
k8s/monitoring/grafana-helm/values-cloud.yaml.tmpl— Helm values rendered bydeploy-local.shwhenmonitoring.mode: cloud; destinations are Grafana Cloud Tempo/Mimir/Loki. - Local:
k8s/monitoring/grafana/local/configmap.yaml— hand-rolled Alloy configmap applied whenmonitoring.mode: local; destinations are in-cluster Jaeger, Prometheus, and Loki.
./deploy-local.sh selects the correct values file / configmap based on monitoring.mode in
conf.yml.
Rationale:
- A single configmap with conditional blocks or “empty endpoint = no-op” logic obscures intent. Operators reading the deployed configmap should see exactly what is running.
- Cloud and local pipelines have structurally different exporters (
otelcol.exporter.otlp+otelcol.auth.basicvsotelcol.exporter.otlpwithtls.insecure = true+prometheus.remote_write). These are not cosmetic differences. - The split prevents accidental cloud credential exposure in local-only deployments.
Alternative considered: Single configmap with empty-endpoint guards — rejected because it conflates two deployment modes and produces misleading no-op exporter logs.
Addendum (single-sourced trace correlation): The one piece of logic that was genuinely identical
in both configs — the
trace-ID/span-ID → Loki structured-metadata correlation stages
— is now authored once at k8s/monitoring/grafana/shared/trace-correlation-stages.alloy and spliced
into both configmap.yaml.tmpl (raw) and values-cloud.yaml.tmpl (Helm-tpl-escaped) by
deploy-local.sh (render_local_alloy_configmap() / render_helm_values()). This doesn’t change
the rationale above — the two pipelines still have separate, structurally different files; it just
stops one specific snippet from silently drifting when only one side gets edited.