Skip to content

Onboard Grafana Alloy via remotecfg

This is the Standards onboarding path for Grafana Alloy: no LinkMesh agent on the host. You install upstream Alloy and add a small remotecfg block that pulls config from LinkMesh’s CollectorService. Alloy authenticates with a per-collector bearer token you mint in the UI.

How it differs from the OpAMP path

Alloy doesn’t use OpAMP for config — it uses its own remotecfg component, which polls LinkMesh over Connect-RPC. So the auth model is slightly different: instead of an enrollment token on a WebSocket upgrade, Alloy carries a per-collector OTLP bearer token that you mint once and paste into the config. The same token also authenticates the optional own_metrics push.

1. Install Grafana Alloy

Terminal window
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/grafana.gpg
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update && sudo apt-get install -y alloy

This installs an alloy systemd service that reads /etc/alloy/config.alloy.

2. Create the collector and mint a token

In the LinkMesh UI: Collectors → + Add Collector, choose the Grafana Alloy option (a collector in alloy-remotecfg mode). On the new collector’s detail page, the enrollment banner’s Grafana Alloy tab mints a per-collector OTLP token and shows the ready-to-paste config. Copy the token (and note the collector’s id).

3. Write config.alloy

Create /etc/alloy/config.alloy. Point remotecfg.url at the server base URL — Alloy appends the CollectorService path itself, so do not add /v1/opamp (that’s the OpAMP endpoint, a different protocol).

logging {
level = "info"
}
remotecfg {
url = "https://linkmesh.example.com"
id = constants.hostname
poll_frequency = "60s"
// Per-collector bearer token minted in the LinkMesh UI (step 2).
// LinkMesh's remotecfg auth accepts ONLY the Bearer scheme — not basic_auth.
bearer_token = "<COLLECTOR_OTLP_TOKEN>"
}
// ── Optional: own_metrics → LinkMesh, so the topology canvas shows
// per-component throughput for this Alloy collector. ───────────────
prometheus.exporter.self "default" { }
prometheus.scrape "linkmesh_self" {
targets = prometheus.exporter.self.default.targets
forward_to = [otelcol.receiver.prometheus.linkmesh.receiver]
scrape_interval = "30s"
}
otelcol.receiver.prometheus "linkmesh" {
output {
metrics = [otelcol.exporter.otlphttp.linkmesh.input]
}
}
otelcol.exporter.otlphttp "linkmesh" {
client {
endpoint = "https://linkmesh.example.com"
headers = {
"Authorization" = "Bearer <COLLECTOR_OTLP_TOKEN>",
}
}
}

The same token goes in both places: remotecfg.bearer_token (pulling config) and the own_metrics exporter’s Authorization header (pushing throughput). If you skip the own_metrics block, config pull still works — you just won’t see per-component throughput on the canvas.

4. Restart and verify

Terminal window
sudo systemctl restart alloy
# Watch remotecfg fetch its config:
sudo journalctl -u alloy -f | grep -i remotecfg

In the LinkMesh UI:

  • Collectors — the host appears (mode alloy-remotecfg), status leaves awaiting_connection within ~60s (one poll interval).
  • Its Events tab shows remotecfg_registered, then config delivery.
  • The topology canvas renders throughput once own_metrics start landing.

Troubleshooting

  • 401 / Unauthenticated from remotecfg — the token is wrong or was rotated. Re-mint on the collector page and update bearer_token.
  • Alloy connects but gets no pipeline — confirm the collector has sources and a route configured in LinkMesh; an empty config renders an empty pipeline.
  • Nothing on the canvas — the own_metrics block is missing or its Authorization token differs from remotecfg.bearer_token; they must match.
  • More: Enrollment troubleshooting.