1. Configuration blocks
Attributes, blocks and expressions in River language.
Understanding the syntax
Your alloy config is written in a file (usually config.alloy) using a declarative syntax strongly inspired by Terraform. In this alloy configuration, a component is defined by a name and an identifier. Inside the block, you define attributes (e.g., url = "http://...") and potentially sub-blocks. Components export values that can be referenced by other components (like alloy prometheus or prometheus.exporter.self), creating the pipeline.
prometheus.scrape "default" {
targets = [{"__address__" = "localhost:9090"}]
forward_to = [prometheus.remote_write.mimir.receiver]
}
Comma management
In River, comma management in arrays and definitions is very flexible. It is recommended to add a comma after the last element of a list or multi-line configuration (trailing comma). This makes it easier to add subsequent elements and significantly reduces the risk of conflicts during code merges (Git merges).
prometheus.exporter.unix "example" {
enable_collectors = [
"cpu",
"meminfo",
"diskstats",
"textfile",
]
textfile {
directory = "/var/lib/node_exporter/textfile_collector"
}
}
2. River vs HCL
Differences and specifics of the River language compared to HCL.
Inspiration and operation
Although alloy river is largely inspired by the HCL (HashiCorp Configuration Language) used by Terraform (similar syntax with blocks and attributes), it is specifically designed for continuous observability. Unlike HCL, which is often used for static infrastructure deployments (evaluated once during an apply), River maintains a dynamic directed acyclic graph (DAG). When a variable or secret is modified, River automatically and continuously re-evaluates all dependent components, without requiring a restart.
3. Variables and Functions
Using logic to make configurations dynamic.
Making the configuration dynamic
The River language allows you to use expressions to make your configuration dynamic. You can call built-in functions (like sys.env("VAR_NAME") to read environment variables, or string manipulation functions) and define local configuration blocks (local.file) to reuse values, thus avoiding repetition and facilitating maintenance.
4. Secrets Management
Integrating environment variables and vault files.
Securing access
It is crucial not to store passwords or tokens in plain text in your configuration files. Alloy offers several methods to manage secrets: using the sys.env() function, reading from secure files on disk, or integrating with third-party secret management solutions via specific components. Values marked as secret are never displayed in plain text in logs or the UI.
Best Practice: Use the local.file component to read dynamically mounted secrets (like Kubernetes or Vault secrets) so that Alloy reacts automatically if the secret file changes without restarting the process.Common Mistake: Hardcoding API tokens in the configuration file. This exposes the secret in your Git repository and in plain text in Alloy's UI (on port 12345).
local.file "mimir_api_key" {
filename = "/etc/alloy/secrets/mimir_token.txt"
is_secret = true
}
prometheus.remote_write "cloud" {
endpoint {
url = "https://mimir-url/api/v1/push"
basic_auth {
username = "admin"
password = local.file.mimir_api_key.content
}
}
}Here is also an example of direct integration with HashiCorp Vault using the remote.vault component:
remote.vault "api_token" {
server = "https://vault.internal:8200"
path = "secret/data/alloy/api"
auth {
token = sys.env("VAULT_TOKEN")
}
}
prometheus.remote_write "cloud" {
endpoint {
url = "https://mimir-url/api/v1/push"
basic_auth {
username = "admin"
password = remote.vault.api_token.data["password"]
}
}
}
5. Alloy Observability
Monitor the health of your own pipelines via the native UI interface.
The local user interface
Alloy embeds an HTTP server (via alloy port 12345 by default) that exposes the grafana alloy ui, a very useful graphical interface. This UI allows you to visualize the graph (DAG) of your components, check their state (Healthy/Unhealthy), and inspect the evaluated arguments and exports of each component in real-time. It is the essential tool for alloy live debugging of your configurations.