Grafana Alloy
Grafana Alloy is a vendor-neutral telemetry collector that uses the River configuration language. It can scrape Prometheus exporters, receive OTLP data, and forward everything to xScaler over remote_write.
:::warning Required headers Both headers are mandatory on every request:
Authorization: Bearer <token>— set via theauthorizationblockX-Scope-OrgID: <tenant-id>— set via theheadersmap :::
Configuration
Save the following as config.alloy:
// Collect host metrics via the unix exporter
prometheus.exporter.unix "host" {}
prometheus.scrape "host_metrics" {
targets = prometheus.exporter.unix.host.targets
forward_to = [prometheus.remote_write.xscaler.receiver]
scrape_interval = "15s"
}
prometheus.remote_write "xscaler" {
endpoint {
url = "https://euw1-01.m.xscalerlabs.com/api/v1/push"
authorization {
type = "Bearer"
credentials = "<token>"
}
headers = {
"X-Scope-OrgID" = "<tenant-id>",
}
queue_config {
capacity = 10000
max_samples_per_send = 2000
batch_send_deadline = "5s"
min_shards = 1
max_shards = 10
min_backoff = "30ms"
max_backoff = "5s"
}
}
}
River syntax notes
- River uses
=for assignment, not:. - Blocks are delimited by
{ }, not by indentation. - The
authorizationblock takestypeandcredentialsas separate fields. headersis a string map — keys and values are both quoted strings.- The
forward_toreference inprometheus.scrapewires the scrape output directly to theremote_writereceiver. The labelxscalermust match the label onprometheus.remote_write.
Load credentials from environment variables
Avoid storing secrets in the config file by reading them at runtime:
prometheus.remote_write "xscaler" {
endpoint {
url = "https://euw1-01.m.xscalerlabs.com/api/v1/push"
authorization {
type = "Bearer"
credentials = env("XSCALER_TOKEN")
}
headers = {
"X-Scope-OrgID" = env("XSCALER_TENANT_ID"),
}
}
}
Run with Docker
docker run --rm \
-e XSCALER_TOKEN=<token> \
-e XSCALER_TENANT_ID=<tenant-id> \
-v $(pwd)/config.alloy:/etc/alloy/config.alloy \
grafana/alloy:latest \
run /etc/alloy/config.alloy
Troubleshooting
Metrics not arriving
- Enable debug logging by adding
--stability.level=generally-availableto thealloy runcommand and checking stdout for errors. - Open the Alloy UI at
http://localhost:12345— it shows the health status of every component. A red component means it has encountered an error. - Verify the
headersblock includes"X-Scope-OrgID"— this is the most common cause of400 no org iderrors. - Confirm the
authorizationblock usestype = "Bearer"andcredentials = "<token>"(not a barebearer_tokenfield at the top level when using the block form).
400 Bad Request — "no org id"
The X-Scope-OrgID key is missing from the headers map. Add it and restart Alloy.
401 Unauthorized
Check the credentials value — it must be the raw token string, not Bearer <token> (the type = "Bearer" prefix is added automatically by Alloy).