OpenTelemetry Environment Variables Reference
Environment variables configure the OpenTelemetry SDK without touching code. Every SDK reads the same names, so the same deployment manifest works across a polyglot fleet, and moving from a local Collector to a hosted backend is a configuration change rather than a rebuild.
Code-level configuration overrides environment variables. If a setting appears to be ignored, something in the code is setting it explicitly.
Quick start
Four variables cover most deployments:
export OTEL_SERVICE_NAME=checkout-api
export OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3,deployment.environment.name=production
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=https://<secret>@api.uptrace.dev?grpc=4317
Service identity
| Variable | Default | Description |
|---|---|---|
OTEL_SERVICE_NAME | unknown_service | Logical service name. The primary key for grouping telemetry |
OTEL_RESOURCE_ATTRIBUTES | — | Comma-separated key=value resource attributes |
OTEL_SERVICE_NAME takes precedence over a service.name set inside OTEL_RESOURCE_ATTRIBUTES.
Set the name to something stable across deploys. Anything containing a pod name, a container id, or a build number splits one service into many in every dashboard.
export OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3,deployment.environment.name=production,service.namespace=shop
Use semantic conventions for the keys. Note deployment.environment.name — the older deployment.environment was renamed.
Exporter
| Variable | Default | Description |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | http://localhost:4317 (gRPC), http://localhost:4318 (HTTP) | Where to send telemetry |
OTEL_EXPORTER_OTLP_PROTOCOL | http/protobuf | grpc, http/protobuf, or http/json |
OTEL_EXPORTER_OTLP_HEADERS | — | key1=value1,key2=value2. Usually authentication |
OTEL_EXPORTER_OTLP_TIMEOUT | 10000 | Milliseconds to wait for each batch export |
OTEL_EXPORTER_OTLP_COMPRESSION | — | none or gzip |
The default protocol in the specification is http/protobuf, but individual SDKs have defaulted to grpc at various points. Set it explicitly rather than relying on the default.
The endpoint path trap
The generic and signal-specific endpoint variables behave differently over HTTP, and this catches people regularly:
| Variable | Behavior over HTTP |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | The signal path is appended — v1/traces, v1/metrics, v1/logs |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | Used as-is. You must include /v1/traces yourself |
# Generic: the SDK sends to https://collector.example.com/v1/traces
export OTEL_EXPORTER_OTLP_ENDPOINT=https://collector.example.com
# Signal-specific: the full path is required
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://collector.example.com/v1/traces
Setting the signal-specific variable to a bare host produces exports to / and a 404 from the receiver.
Per-signal overrides exist for every setting: OTEL_EXPORTER_OTLP_TRACES_HEADERS, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT, OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, and so on. They override the generic variable for that signal only.
Selecting exporters
| Variable | Default | Values |
|---|---|---|
OTEL_TRACES_EXPORTER | otlp | otlp, zipkin, console, none |
OTEL_METRICS_EXPORTER | otlp | otlp, prometheus, console, none |
OTEL_LOGS_EXPORTER | otlp | otlp, console, none |
console prints spans to stdout, which is the fastest way to confirm instrumentation works before a backend is involved. none disables a signal without removing its instrumentation.
Some agents ship with OTEL_LOGS_EXPORTER defaulting to none. If traces arrive and logs do not, check this first.
TLS
| Variable | Description |
|---|---|
OTEL_EXPORTER_OTLP_CERTIFICATE | Path to the CA certificate for verifying the server |
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE | Path to the client certificate, for mTLS |
OTEL_EXPORTER_OTLP_CLIENT_KEY | Path to the client private key, for mTLS |
OTEL_EXPORTER_OTLP_INSECURE | true disables transport security. Local Collectors only |
Sampling
| Variable | Default | Description |
|---|---|---|
OTEL_TRACES_SAMPLER | parentbased_always_on | Sampler name |
OTEL_TRACES_SAMPLER_ARG | — | Argument, meaning depends on the sampler |
Accepted samplers: always_on, always_off, traceidratio, parentbased_always_on, parentbased_always_off, parentbased_traceidratio, jaeger_remote, parentbased_jaeger_remote.
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1 # keep 10%
Prefer the parentbased_ variants. Without them each service decides independently and traces come out fragmented. See sampling.
Propagation
| Variable | Default | Description |
|---|---|---|
OTEL_PROPAGATORS | tracecontext,baggage | Comma-separated propagator list |
Values: tracecontext, baggage, b3, b3multi, jaeger, xray, ottrace, none. Listing several builds a composite that extracts whichever format arrives and injects all of them — which is how you migrate formats without breaking traces. See context propagation.
Batching
Spans and logs are buffered and exported in batches. These control the buffer.
| Variable | Default | Description |
|---|---|---|
OTEL_BSP_SCHEDULE_DELAY | 5000 | Milliseconds between span batch exports |
OTEL_BSP_EXPORT_TIMEOUT | 30000 | Milliseconds allowed per span export |
OTEL_BSP_MAX_QUEUE_SIZE | 2048 | Spans buffered before new ones are dropped |
OTEL_BSP_MAX_EXPORT_BATCH_SIZE | 512 | Spans per export request |
OTEL_BLRP_SCHEDULE_DELAY | 1000 | Same, for log records |
OTEL_BLRP_EXPORT_TIMEOUT | 30000 | |
OTEL_BLRP_MAX_QUEUE_SIZE | 2048 | |
OTEL_BLRP_MAX_EXPORT_BATCH_SIZE | 512 | |
OTEL_METRIC_EXPORT_INTERVAL | 60000 | Milliseconds between metric exports |
The queue size is the one to watch. When it fills, spans are dropped silently — the symptom is missing spans under load with no errors anywhere. Raise OTEL_BSP_MAX_QUEUE_SIZE for high-throughput services, and remember it is a memory ceiling: queue size times average span size.
Limits
Guards against a single pathological span exhausting memory or being rejected by the backend.
| Variable | Default | Description |
|---|---|---|
OTEL_ATTRIBUTE_COUNT_LIMIT | 128 | Attributes per span, log record, or resource |
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT | unlimited | Characters per attribute value |
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT | 128 | Overrides the global limit for spans |
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT | unlimited | Overrides the global value limit for spans |
OTEL_SPAN_EVENT_COUNT_LIMIT | 128 | Events per span |
OTEL_SPAN_LINK_COUNT_LIMIT | 128 | Links per span |
Truncation is silent. If attribute values are being cut off at a suspiciously round number, a length limit is set somewhere.
Setting OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT is worthwhile when instrumentation records SQL statements or request bodies — a single unbounded value can dwarf the rest of the span.
Disabling
| Variable | Default | Description |
|---|---|---|
OTEL_SDK_DISABLED | false | true turns the SDK into a no-op for all signals |
Useful for tests and for turning instrumentation off in an incident without redeploying. Instrumentation calls still execute, so overhead is reduced rather than eliminated.
Removed and deprecated
Configuration copied from older tutorials often includes variables that no longer do anything:
| Variable | Status |
|---|---|
OTEL_EXPORTER_JAEGER_ENDPOINT | Removed. The Jaeger exporter is gone from the SDKs |
OTEL_EXPORTER_JAEGER_USER | Removed |
OTEL_EXPORTER_JAEGER_PASSWORD | Removed |
jaeger value for OTEL_PROPAGATORS | Deprecated, still functional for legacy interop |
Jaeger has accepted OTLP natively for years, so exporting to it means pointing OTEL_EXPORTER_OTLP_ENDPOINT at Jaeger's OTLP port. There is no Jaeger-specific configuration any more.
Per-environment configuration
Development — see everything, locally:
export OTEL_SERVICE_NAME=checkout-api
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=development
export OTEL_TRACES_SAMPLER=always_on
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_INSECURE=true
Swap the exporter to console to check instrumentation without running a Collector at all:
export OTEL_TRACES_EXPORTER=console
Production — sample, batch larger, secure the transport:
export OTEL_SERVICE_NAME=checkout-api
export OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3,deployment.environment.name=production
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=https://<secret>@api.uptrace.dev?grpc=4317
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
export OTEL_BSP_MAX_QUEUE_SIZE=4096
OTEL_EXPORTER_OTLP_HEADERS carries credentials. Inject it from a secret store rather than committing it to a manifest.
What's next?
- Get started with OpenTelemetry — SDK setup per language
- OpenTelemetry sampling — what the sampler variables control
- OpenTelemetry context propagation — what the propagator variable controls
- OpenTelemetry Collector — the usual destination for the endpoint
- Collector exporters — the same settings on the Collector side
- OpenTelemetry troubleshooting — when the endpoint is right and nothing arrives
- OpenTelemetry APM — the backend behind the endpoint