1. L'essentiel : Alloy n'est pas un SDK #
Qui lit quelle variable, et pourquoi la confusion est fréquente.
Trois familles de variables à ne pas confondre #
Une idée reçue revient souvent : « Alloy est compatible OpenTelemetry, donc il suffit de définir les variables OTEL_* ». C'est faux. Ces variables sont définies par la spécification pour les SDK, c'est-à-dire les bibliothèques intégrées à vos applications. Alloy est un collecteur : il reçoit de l'OTLP, mais sa configuration passe par ses composants.
| Famille | Exemples | Qui la lit ? |
|---|---|---|
| Variables natives d’Alloy | GOMEMLIMIT, GOGC, HTTP_PROXY | Alloy, automatiquement |
| Vos propres variables | TEMPO_ENDPOINT, CLUSTER_NAME | Alloy, seulement via sys.env() |
| Variables OpenTelemetry | OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT | Le SDK de l’application |
| L’exception | OTEL_RESOURCE_ATTRIBUTES | Alloy, via otelcol.processor.resourcedetection |
Comment ces résultats ont été vérifiés : les comportements de cet article ont été testés le 4 novembre 2026 avec Grafana Alloy v1.21.3, en envoyant de vraies traces OTLP. La liste des variables évolue : consultez la documentation de votre version.
2. Les variables lues nativement par Alloy #
Mémoire, runtime Go, proxy et paquets Linux.
Mémoire et runtime Go #
Alloy étant écrit en Go, il respecte les variables du runtime. Ce sont les seules, avec celles du proxy, que la documentation d'Alloy liste comme lues automatiquement.
| Variable | Rôle | Défaut |
|---|---|---|
GOMEMLIMIT | Limite mémoire souple du ramasse-miettes (900MiB, 1GiB…) | Calculée (voir ci-dessous) |
AUTOMEMLIMIT | Part de la limite du conteneur utilisée pour calculer GOMEMLIMIT (0 à 1) | 0,9 |
AUTOMEMLIMIT_EXPERIMENT | system : se baser sur la mémoire totale du système | – |
GOGC | Pourcentage cible du ramasse-miettes ; off le désactive | 100 |
GOMAXPROCS | Nombre de threads exécutant du code Go simultanément | Non précisé |
GOTRACEBACK | Détail des traces lors d’un panic | Non précisé |
GODEBUG | Options de débogage : x509usefallbackroots, netdns | – |
PPROF_MUTEX_PROFILING_PERCENT | Part des mutex échantillonnés | 0,1 % |
PPROF_BLOCK_PROFILING_RATE | Fréquence de suivi des blocages de goroutines | 10000 |
GOMEMLIMIT n'est pas une limite stricte : la documentation le rappelle explicitement. Gardez une marge sous la limite de votre conteneur, sinon le processus peut quand même être tué (OOM).Proxy sortant #
| Variable | Rôle |
|---|---|
HTTP_PROXY | Proxy des requêtes HTTP |
HTTPS_PROXY | Proxy des requêtes HTTPS |
NO_PROXY | Exceptions séparées par des virgules : adresses IP, blocs CIDR, domaines ou * |
Paquets Linux (systemd) #
Avec les paquets deb et rpm, le service lit ses réglages dans /etc/default/alloy (Debian, Ubuntu) ou /etc/sysconfig/alloy (RHEL, Fedora, SUSE). Après modification : sudo systemctl restart alloy.
| Variable | Rôle |
|---|---|
CONFIG_FILE | Fichier de configuration du moteur par défaut |
CUSTOM_ARGS | Options supplémentaires, par exemple --server.http.listen-addr=0.0.0.0:12345 |
ALLOY_OTEL_MODE | Active le moteur OpenTelemetry au lieu du moteur par défaut |
OTEL_CONFIG_FILE, OTEL_CUSTOM_ARGS | Équivalents pour le moteur OpenTelemetry |
3. Vos variables : sys.env() #
Lecture explicite, valeurs par défaut et pièges.
Lire une variable dans la configuration #
Toute autre variable doit être lue explicitement avec sys.env(). Si elle n'existe pas, la fonction renvoie une chaîne vide, sans erreur : c'est pourquoi on l'associe à coalesce() pour prévoir une valeur par défaut.
otelcol.exporter.otlp "tempo" {
client {
// host:port pour cet exportateur gRPC ; valeur par défaut si la variable est absente
endpoint = coalesce(sys.env("TEMPO_ENDPOINT"), "tempo:4317")
headers = { "X-Scope-OrgID" = coalesce(sys.env("TEMPO_TENANT"), "anonymous") }
tls {
insecure = true
}
}
}Piège : sans valeur par défaut, l'échec survient plus loin. Avecendpoint = sys.env("ABSENTE")surotelcol.exporter.otlphttp,alloy runs'arrête sur « at least one endpoint must be specified ». Le message ne cite pas la variable : pensez à elle en premier.
Une variable d'environnement n'est lue qu'au démarrage du processus : la changer impose de redémarrer Alloy. Pour une valeur qui doit pouvoir changer à chaud, utilisez local.file ou remote.http. Les secrets sont traités dans la leçon Gestion des secrets.
4. OTEL_RESOURCE_ATTRIBUTES : la variable que l'on peut lire #
Le détecteur env, l'option override et son piège.
Le composant resourcedetection #
Le composant otelcol.processor.resourcedetection possède un détecteur env, activé par défaut (detectors = ["env"]). Il lit OTEL_RESOURCE_ATTRIBUTES au format cle1=valeur1,cle2=valeur2 et ajoute ces attributs à toutes les données qui le traversent. C'est le moyen d'indiquer l'environnement ou le cluster une seule fois, au niveau du collecteur.
Le piège d'override #
Ces quatre résultats ont été mesurés avec une même trace envoyée par une application nommée from-app. Trois enseignements :
- Sans le composant, la variable n'a aucun effet. La définir pour Alloy ne suffit pas.
overridevauttruepar défaut : un attribut déjà présent est remplacé. Si la variable contientservice.name, toutes les applications qui passent par cet Alloy prennent ce nom. Vos services fusionnent alors dans Tempo.OTEL_SERVICE_NAMEest ignorée par ce détecteur, contrairement à un SDK où elle a priorité.
Bonne pratique : sur un Alloy qui reçoit plusieurs applications, utilisezoverride = falseet limitez la variable aux attributs du site :deployment.environment.name,k8s.cluster.name,cloud.region. Ne mettez jamaisservice.name.
5. Les variables OTEL_* côté application #
Pointer un SDK vers Alloy avec les variables standard.
Variables générales du SDK #
| Variable | Rôle | Défaut (spécification) |
|---|---|---|
OTEL_SERVICE_NAME | Nom du service ; prioritaire sur le service.name de OTEL_RESOURCE_ATTRIBUTES | – |
OTEL_RESOURCE_ATTRIBUTES | Attributs de ressource : cle=valeur,cle2=valeur2 | – |
OTEL_SDK_DISABLED | Désactive le SDK | false |
OTEL_LOG_LEVEL | Niveau de journalisation du SDK | info |
OTEL_PROPAGATORS | Propagation du contexte | tracecontext,baggage |
OTEL_TRACES_SAMPLER | Échantillonneur | parentbased_always_on |
OTEL_TRACES_SAMPLER_ARG | Argument de l’échantillonneur (ex. 0.1) | – |
OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTER | Exportateur de chaque signal | otlp |
Variables de l'exportateur OTLP #
Chacune existe aussi par signal, par exemple OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, qui l'emporte alors sur la variable générale.
| Variable | Rôle | Défaut |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | URL de destination | gRPC http://localhost:4317, HTTP http://localhost:4318 |
OTEL_EXPORTER_OTLP_PROTOCOL | grpc, http/protobuf ou http/json | http/protobuf |
OTEL_EXPORTER_OTLP_HEADERS | En-têtes : cle=valeur,cle2=valeur2 | – |
OTEL_EXPORTER_OTLP_TIMEOUT | Délai maximal d’un envoi, nouvelles tentatives comprises | 10s |
OTEL_EXPORTER_OTLP_COMPRESSION | gzip ou none | Choix du SDK |
OTEL_EXPORTER_OTLP_CERTIFICATE | Certificat de confiance TLS | – |
Le piège du chemin : avecOTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4318, le SDK ajoute/v1/traces. Avec la variable par signalOTEL_EXPORTER_OTLP_TRACES_ENDPOINT, l'URL est utilisée telle quelle : indiquez alorshttp://alloy:4318/v1/traces. Autre erreur fréquente : envoyer duhttp/protobuf(protocole par défaut) sur le port gRPC4317.
Exemple complet : une application et Alloy #
L'application reçoit ses variables standard ; Alloy reçoit celles qui concernent le site. Sa configuration, validée avec alloy validate et testée de bout en bout, reprend explicitement la destination :
otelcol.receiver.otlp "default" {
grpc {
endpoint = "0.0.0.0:4317"
}
http {
endpoint = "0.0.0.0:4318"
}
output {
traces = [otelcol.processor.resourcedetection.env.input]
metrics = [otelcol.processor.resourcedetection.env.input]
logs = [otelcol.processor.resourcedetection.env.input]
}
}
// Ajoute les attributs de OTEL_RESOURCE_ATTRIBUTES sans écraser ceux des applications
otelcol.processor.resourcedetection "env" {
detectors = ["env"]
override = false
output {
traces = [otelcol.processor.batch.default.input]
metrics = [otelcol.processor.batch.default.input]
logs = [otelcol.processor.batch.default.input]
}
}
otelcol.processor.batch "default" {
output {
traces = [otelcol.exporter.otlphttp.backend.input]
metrics = [otelcol.exporter.otlphttp.backend.input]
logs = [otelcol.exporter.otlphttp.backend.input]
}
}
// Reprend explicitement la variable standard : sans sys.env, elle ne serait pas lue
otelcol.exporter.otlphttp "backend" {
client {
endpoint = sys.env("OTEL_EXPORTER_OTLP_ENDPOINT")
headers = { "X-Scope-OrgID" = coalesce(sys.env("BACKEND_TENANT"), "anonymous") }
}
}services:
alloy:
image: grafana/alloy:latest
command: run /etc/alloy/config.alloy --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data
volumes:
- ./config.alloy:/etc/alloy/config.alloy:ro
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: http://tempo:4318
OTEL_RESOURCE_ATTRIBUTES: deployment.environment.name=prod,k8s.cluster.name=eu-west
GOMEMLIMIT: 900MiB
deploy:
resources:
limits:
memory: 1G
app:
image: mon-registre/commandes:1.4.0
environment:
OTEL_SERVICE_NAME: commandes
OTEL_RESOURCE_ATTRIBUTES: service.version=1.4.0,team=paiements
OTEL_EXPORTER_OTLP_ENDPOINT: http://alloy:4318
OTEL_EXPORTER_OTLP_PROTOCOL: http/protobuf
OTEL_TRACES_SAMPLER: parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG: "0.1"À l'essai, la trace arrive bien sur /v1/traces du backend : otelcol.exporter.otlphttp ajoute le chemin de chaque signal, comme un SDK avec la variable générale.
Correspondance :otelcol.exporter.otlphttpattend une URL (http://tempo:4318) etotelcol.exporter.otlp, en gRPC, unhost:port(tempo:4317). Sansendpoint, ce dernier est refusé au chargement (« missing required attribute endpoint »), même siOTEL_EXPORTER_OTLP_ENDPOINTest définie.
6. Le moteur OpenTelemetry : alloy otel #
La syntaxe ${env:VAR} du Collector, en préversion publique.
Une configuration YAML du Collector #
Le binaire d'Alloy embarque aussi un moteur OpenTelemetry, lancé avec alloy otel --config=otel.yaml. Il lit un fichier YAML au format du Collector : on y retrouve la syntaxe ${env:VARIABLE}, au lieu de sys.env(). Nous avons vérifié cette substitution et le détecteur env sur Alloy v1.21.3.
receivers:
otlp:
protocols:
http:
endpoint: ${env:RECEIVER_ENDPOINT}
processors:
resourcedetection:
detectors: [env]
exporters:
otlphttp:
endpoint: ${env:OTEL_EXPORTER_OTLP_ENDPOINT}
service:
pipelines:
traces:
receivers: [otlp]
processors: [resourcedetection]
exporters: [otlphttp]Ce moteur est en préversion publique : la documentation prévient que des changements incompatibles sont possibles. Pour la production, le moteur par défaut (alloy run) reste le choix stable.7. Kubernetes et bonnes pratiques #
Helm, Beyla, erreurs courantes et commandes.
Sur Kubernetes #
# values.yaml du chart Helm grafana/alloy
alloy:
extraEnv:
- name: OTEL_RESOURCE_ATTRIBUTES
value: "deployment.environment.name=prod,k8s.cluster.name=eu-west"
- name: GOMEMLIMIT
value: "900MiB"
resources:
limits:
memory: 1GiCas particulier de Beyla : le composant beyla.ebpf peut reprendre, via extra_resource_labels, des attributs fournis par la variable OTEL_RESOURCE_ATTRIBUTES du service observé. C'est la variable de l'application qui compte, pas celle d'Alloy.
À retenir #
- Définir
OTEL_*pour Alloy ne configure pas Alloy, saufOTEL_RESOURCE_ATTRIBUTESavecresourcedetection. - Lisez vos valeurs avec
sys.env(), et prévoyez une valeur par défaut aveccoalesce(). - Utilisez
override = falsesur un Alloy partagé, et jamaisservice.namedans sa variable. - Réservez
OTEL_SERVICE_NAMEetOTEL_EXPORTER_OTLP_*aux applications. - Fixez
GOMEMLIMITsous la limite mémoire du conteneur.
Commandes à connaître #
# Vérifier la configuration avec les variables de l'environnement courant
TEMPO_ENDPOINT=tempo:4317 alloy validate config.alloy
# Valider un fichier sans le lancer, puis le démarrer
alloy fmt config.alloy
alloy run config.alloy --server.http.listen-addr=127.0.0.1:12345
# Voir les variables réellement reçues par le processus
docker compose exec alloy env | grep -E 'OTEL_|GOMEM'
# Tester le pipeline avec une trace minimale (OTLP/HTTP)
curl -X POST -H 'Content-Type: application/json' -d @trace.json http://localhost:4318/v1/traces
# Moteur OpenTelemetry (préversion publique)
alloy otel --config=otel.yaml