8.3 OpenTelemetry SDK Configuration & Environment Variables
Key Takeaways
OpenTelemetry adheres to 12-factor cloud-native application standards by enabling comprehensive SDK configuration through standardized environment variables without code modification.
Signal-specific exporter environment variables (e.g., OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) strictly override generic exporter variables (OTEL_EXPORTER_OTLP_ENDPOINT).
OTEL_SERVICE_NAME sets the primary logical identity of the service and takes strict precedence over any service.name defined within OTEL_RESOURCE_ATTRIBUTES.
Sampler configuration via OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG lets operators deploy ParentBased and ratio-based sampling strategies across environments without code changes.
The BatchSpanProcessor can be tuned for throughput, queue depth, and memory safety using variables including OTEL_BSP_SCHEDULE_DELAY, OTEL_BSP_MAX_QUEUE_SIZE, and OTEL_BSP_MAX_EXPORT_BATCH_SIZE.
8.3 OpenTelemetry SDK Configuration & Environment Variables
Quick Answer: The OpenTelemetry SDK adheres strictly to 12-factor application methodology by providing code-free configuration via standardized
OTEL_*environment variables. This enables operators to modify service identity, exporters, protocols, endpoints, samplers, and processor buffer sizing without modifying or recompiling application code. Crucially, signal-specific variables (such asOTEL_EXPORTER_OTLP_TRACES_ENDPOINT) strictly override generic fallback variables (OTEL_EXPORTER_OTLP_ENDPOINT), andOTEL_SERVICE_NAMEtakes strict precedence over anyservice.namedeclared insideOTEL_RESOURCE_ATTRIBUTES.
In modern cloud-native deployment patterns, application binaries and container images are immutable artifacts promoted sequentially through development, staging, canary, and production environments. Hardcoding telemetry configurations—such as backend endpoints, authentication tokens, sampling ratios, or batching queues—into source code violates basic operational practices. OpenTelemetry resolves this by defining a cross-language specification for SDK configuration through operating system environment variables, plus a YAML file format for richer setups (covered at the end of this section).
The specification defines one common set of environment variables with shared parsing and precedence rules. Language SDKs implement most of them; the specification's compliance matrix records the gaps for each language.
Essential General SDK Environment Variables
Before exploring signal-specific transport pipelines, candidates must master the fundamental environment variables that establish service identity, context propagation, and diagnostic visibility.
1. OTEL_SERVICE_NAME
- Purpose: Defines the logical service name associated with all telemetry emitted by the process (e.g.,
checkout-service,auth-worker). - Default: If
service.nameis not set anywhere, the SDK usesunknown_service:plus the process executable name (e.g.,unknown_service:java), or plainunknown_serviceif the executable name is unavailable. - Critical Precedence Rule: If both
OTEL_SERVICE_NAMEandOTEL_RESOURCE_ATTRIBUTESdefine a service name,OTEL_SERVICE_NAMEtakes absolute precedence!# In this scenario, service.name resolves to 'order-processor' export OTEL_SERVICE_NAME="order-processor" export OTEL_RESOURCE_ATTRIBUTES="service.name=legacy-billing,deployment.environment.name=prod"
2. OTEL_RESOURCE_ATTRIBUTES
- Purpose: A comma-separated list of key-value pairs defining resource entity attributes that describe the host, container, cloud platform, and application version.
- Syntax:
key1=val1,key2=val2 - Example:
export OTEL_RESOURCE_ATTRIBUTES="service.version=3.4.1,deployment.environment.name=production,host.id=ip-10-0-4-12,k8s.cluster.name=prod-us-east" - Character Escaping: Values containing commas, spaces, equals signs, or other reserved characters must be percent-encoded (for example
%2Cfor a comma); quoting or backslash escaping is not part of the format.
3. OTEL_LOG_LEVEL
- Purpose: Sets the diagnostic log verbosity of the internal OpenTelemetry SDK itself (controls the SDK's internal logging for initialization, connection dropped, or queue overflow warnings). It does not filter or control the application's business log output!
- Accepted Values: Language-specific level names such as
debug,info,warn, anderror(Node.js, for example, also acceptsnone,verbose, andall). - Default:
info. - Operational Troubleshooting: When an application starts but spans never arrive at the Collector, setting
OTEL_LOG_LEVEL=debugimmediately exposes whether the OTLP connection failed, timed out, or encountered TLS certificate validation rejections.
4. OTEL_PROPAGATORS
- Purpose: Specifies a comma-separated list of context propagators to register into the global composite TextMapPropagator.
- Default:
"tracecontext,baggage"(W3C Trace Context and W3C Baggage). - Accepted Identifiers:
tracecontext— W3C Trace Context (traceparent,tracestate).baggage— W3C Baggage (baggage).b3— Zipkin B3 Single Header format (b3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}).b3multi— Zipkin B3 Multi-Header format (X-B3-TraceId,X-B3-SpanId,X-B3-Sampled).jaeger— Legacy Jaeger native format (uber-trace-id).xray— AWS X-Ray format (X-Amzn-Trace-Id).ottrace— OpenTracing legacy format.none— Disables context propagation entirely.
- Example:
# Support both modern W3C standards and legacy Zipkin headers export OTEL_PROPAGATORS="tracecontext,baggage,b3multi"
Signal-Specific Protocol & Endpoint Configuration
A central configuration skill is understanding how the OpenTelemetry SDK resolves endpoints, protocols, and headers across different telemetry signals.
The General vs. Signal-Specific Precedence Hierarchy
The specification defines a two-tier configuration model: Generic Fallback Variables and Signal-Specific Override Variables. The rule is absolute: Signal-specific variables always take precedence over generic variables.
Signal-Specific Endpoint (e.g., OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)
│
Is it set?
├── YES ──► Use Signal-Specific Value (exact URL)
└── NO ──► Fall back to Generic Variable
(OTEL_EXPORTER_OTLP_ENDPOINT)
Endpoint Resolution & URL Path Concatenation
Understanding how endpoints and paths are resolved requires knowing whether the transport is gRPC or HTTP:
-
Default Ports:
- OTLP / gRPC default port:
4317 - OTLP / HTTP default port:
4318
- OTLP / gRPC default port:
-
Generic Endpoint Path Concatenation (HTTP only): When you configure the generic
OTEL_EXPORTER_OTLP_ENDPOINTfor an HTTP protocol (such ashttp/protobuf), the SDK automatically appends standard signal path suffixes to the base URL:- Traces:
{generic_endpoint}/v1/traces - Metrics:
{generic_endpoint}/v1/metrics - Logs:
{generic_endpoint}/v1/logs
Example: If
OTEL_EXPORTER_OTLP_ENDPOINT="http://my-collector:4318", the SDK automatically sends traces tohttp://my-collector:4318/v1/traces. - Traces:
-
Signal-Specific Endpoint (No Path Concatenation): When a signal-specific variable is provided, the SDK does not append any path suffix. It uses the provided URL verbatim!
- Example: If
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://trace-gw:4318/custom/trace/path", the SDK sends traces to that exact URL without appending/v1/traces.
- Example: If
Protocol Configuration
- Generic Variable:
OTEL_EXPORTER_OTLP_PROTOCOL - Signal Overrides:
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL,OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,OTEL_EXPORTER_OTLP_LOGS_PROTOCOL - Valid Options:
grpc— OTLP over gRPC (HTTP/2 with binary Protobuf payloads, default port 4317).http/protobuf— OTLP over HTTP with binary Protobuf payloads (default port 4318).http/json— OTLP over HTTP with JSON payloads (useful for debugging, default port 4318).
Headers, Timeouts & Compression
- Headers: Configured as comma-separated key-value pairs.
- Generic:
OTEL_EXPORTER_OTLP_HEADERS="api-key=secret123,x-tenant=engineering" - Signal-specific:
OTEL_EXPORTER_OTLP_TRACES_HEADERS="api-key=trace-secret"
- Generic:
- Timeout: Maximum time in milliseconds to wait before an export network request fails.
- Generic:
OTEL_EXPORTER_OTLP_TIMEOUT(Default:10000= 10 seconds). - Signal-specific:
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT
- Generic:
- Compression: Compression algorithm applied to OTLP network payloads.
- Generic:
OTEL_EXPORTER_OTLP_COMPRESSION(Accepted:gzip,none. Default:noneor language-dependent). - Signal-specific:
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION
- Generic:
Sampler Configuration via Environment Variables
Instead of hardcoding sampler classes in code, the OpenTelemetry SDK allows complete sampling control via two standardized variables:
OTEL_TRACES_SAMPLER: Defines the sampler algorithm.OTEL_TRACES_SAMPLER_ARG: Supplies the optional numeric parameter required by ratio-based samplers.
Standard Sampler Environment Values
Value of OTEL_TRACES_SAMPLER | Required OTEL_TRACES_SAMPLER_ARG | Operational Behavior |
|---|---|---|
always_on | Not applicable | Samples 100% of all traces unconditionally. |
always_off | Not applicable | Drops 100% of all traces unconditionally. |
traceidratio | Float between 0.0 and 1.0 (e.g. 0.05 for 5%) | Standalone deterministic ratio sampling from the TraceId (the algorithm is not standardized across SDKs). Defaults to 1.0 if the argument is omitted. |
parentbased_always_on | Not applicable | Respects parent context if present; if no parent, samples 100%. This is the specification's default sampler. |
parentbased_always_off | Not applicable | Respects parent context if present; if no parent, drops trace. |
parentbased_traceidratio | Float between 0.0 and 1.0 (e.g. 0.10 for 10%) | Production standard: Respects parent context if present; if no parent, evaluates ratio on root trace. |
jaeger_remote / parentbased_jaeger_remote | endpoint, pollingIntervalMs, initialSamplingRate | Fetches sampling strategies from a Jaeger-compatible remote endpoint. |
xray | Not applicable | AWS X-Ray centralized sampling (third-party value). |
# Production deployment example: Sample 5% of root traces, respect parent everywhere else
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.05"
Tuning the BatchSpanProcessor via Environment Variables
The BatchSpanProcessor is the standard production processor that accepts spans from active tracers, queues them in an in-memory buffer, and dispatches them asynchronously to the configured exporter in batches. Fine-tuning the BatchSpanProcessor via environment variables is essential to prevent application latency degradation and memory exhaustion.
Tracer.end() ──► [ BatchSpanProcessor Queue (OTEL_BSP_MAX_QUEUE_SIZE) ]
│
Trigger: OTEL_BSP_SCHEDULE_DELAY
or: OTEL_BSP_MAX_EXPORT_BATCH_SIZE
▼
[ OTLP Exporter Network Request ]
Key Batching Environment Variables
OTEL_BSP_SCHEDULE_DELAY:- Meaning: The delay interval in milliseconds between consecutive buffer flush cycles.
- Default:
5000ms (5 seconds). - Tuning: Lowering this value (e.g., to
1000ms) forces more frequent export cycles, reducing in-memory span residency at the expense of more frequent network requests.
OTEL_BSP_MAX_QUEUE_SIZE:- Meaning: The maximum number of spans that can sit in the internal buffer awaiting export.
- Default:
2048spans. - Tuning: If an application experiences sudden traffic surges and the internal queue reaches capacity, subsequent completed spans are dropped to protect host memory. During high-throughput bursts, operators increase this to
8192or16384if host RAM permits.
OTEL_BSP_MAX_EXPORT_BATCH_SIZE:- Meaning: The maximum number of spans dispatched in a single exporter network payload.
- Default:
512spans. - Constraint: Must be less than or equal to
OTEL_BSP_MAX_QUEUE_SIZE. - Tuning: Increasing batch size improves network efficiency by amortizing HTTP/gRPC frame overhead across more spans.
OTEL_BSP_EXPORT_TIMEOUT:- Meaning: The maximum duration in milliseconds that an export batch attempt may take before being cancelled.
- Default:
30000ms (30 seconds).
Master Reference: Top 20 Standard OpenTelemetry Environment Variables
The following table consolidates 20 core environment variables:
| Variable Name | Default Value | Functional Category | Purpose & Specification Rules |
|---|---|---|---|
OTEL_SERVICE_NAME | unknown_service:<executable> | Identity | Sets logical service name; overrides service.name in OTEL_RESOURCE_ATTRIBUTES |
OTEL_RESOURCE_ATTRIBUTES | Empty | Identity | Comma-separated key-value pairs defining standard entity resource dimensions |
OTEL_LOG_LEVEL | info | Diagnostics | Internal SDK self-diagnostic log verbosity (language-specific values such as debug, info, warn, error) |
OTEL_PROPAGATORS | tracecontext,baggage | Context | Comma-separated list of active propagators (tracecontext, baggage, b3, etc.) |
OTEL_TRACES_SAMPLER | parentbased_always_on | Sampling | Sampling algorithm (parentbased_traceidratio, always_on, always_off, etc.) |
OTEL_TRACES_SAMPLER_ARG | 1.0 | Sampling | Numeric sampling ratio argument for ratio samplers (float string 0.0 to 1.0) |
OTEL_EXPORTER_OTLP_ENDPOINT | http://localhost:4318 or 4317 | Exporter | Generic OTLP target URL; appends /v1/{signal} when using HTTP protocols |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | None | Exporter | Signal-specific trace endpoint override; used verbatim without path concatenation |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT | None | Exporter | Signal-specific metric endpoint override; used verbatim without path concatenation |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | None | Exporter | Signal-specific log endpoint override; used verbatim without path concatenation |
OTEL_EXPORTER_OTLP_PROTOCOL | http/protobuf (spec default; some SDKs keep grpc) | Exporter | Generic wire protocol (grpc, http/protobuf, http/json) |
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL | None | Exporter | Signal-specific wire protocol override for distributed traces |
OTEL_EXPORTER_OTLP_HEADERS | Empty | Exporter | Generic comma-separated key-value headers injected into export requests |
OTEL_EXPORTER_OTLP_TIMEOUT | 10000 (10s) | Exporter | Maximum time in milliseconds before generic export request times out |
OTEL_EXPORTER_OTLP_COMPRESSION | none | Exporter | Compression applied to OTLP payload bodies (gzip, none) |
OTEL_BSP_SCHEDULE_DELAY | 5000 (5s) | Batch Processor | Delay interval in milliseconds between consecutive BatchSpanProcessor flush cycles |
OTEL_BSP_MAX_QUEUE_SIZE | 2048 | Batch Processor | Maximum number of spans buffered in memory awaiting export before drops occur |
OTEL_BSP_MAX_EXPORT_BATCH_SIZE | 512 | Batch Processor | Maximum number of spans transmitted in a single export network payload |
OTEL_BSP_EXPORT_TIMEOUT | 30000 (30s) | Batch Processor | Maximum time in milliseconds allowed for an export batch to complete |
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT | 128 | Limits | Maximum number of attributes allowed per span before subsequent keys are discarded |
Declarative Configuration with OTEL_CONFIG_FILE
Environment variables are flat, so some settings (such as per-view options or several exporters for one signal) cannot be expressed with them. The declarative configuration specification solves this with a YAML file:
- Point the SDK at the file with
OTEL_CONFIG_FILE=/path/to/otel-config.yaml(the olderOTEL_EXPERIMENTAL_CONFIG_FILEname is deprecated). - When
OTEL_CONFIG_FILEis set, all otherOTEL_*environment variables are ignored, except those the file references through substitution syntax such as${OTEL_EXPORTER_OTLP_ENDPOINT}or${VAR:-default}. - The configuration schema itself is stable (experimental properties carry a
/developmentsuffix), but SDK support is still experimental; the Java agent is the most complete implementation today.
file_format: "1.2"
resource:
attributes_list: ${OTEL_RESOURCE_ATTRIBUTES}
propagator:
composite:
- tracecontext:
- baggage:
tracer_provider:
sampler:
parent_based:
root:
always_on:
processors:
- batch:
exporter:
otlp_http:
endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}/v1/traces
One more switch belongs in every toolkit: OTEL_SDK_DISABLED=true makes the SDK a no-op for all signals (propagators configured through OTEL_PROPAGATORS still work), which is useful for quickly ruling telemetry in or out during an incident.
Summary of Key Takeaways
- Configuration through standardized environment variables decouples telemetry control from application source code.
- Signal-specific exporter variables strictly override general OTLP endpoint and protocol settings.
- Service naming follows a strict hierarchy:
OTEL_SERVICE_NAMEtakes precedence overservice.nameinsideOTEL_RESOURCE_ATTRIBUTES. - Production distributed tracing typically uses
parentbased_traceidratioto preserve full traces while keeping data volumes within budget. - A declarative YAML file (
OTEL_CONFIG_FILE) replaces the flat environment variables entirely when it is set. - The
BatchSpanProcessorqueue and batch limits must be tuned in concert to avoid dropping spans under high-throughput production load.
A DevOps engineer configures a containerized microservice with the following environment variables: OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.internal:4318 OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://collector.internal:4317 OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf When the application starts up and emits traces and metrics, how does the OpenTelemetry SDK route these telemetry signals?
Both traces and metrics are transmitted over gRPC to port 4317 because gRPC takes unconditional precedence over HTTP protocols
Traces are transmitted via gRPC to http://collector.internal:4317, while metrics are transmitted via HTTP/protobuf to http://collector.internal:4318/v1/metrics
The SDK throws an initialization error and aborts startup because conflicting endpoints cannot be specified simultaneously
Both traces and metrics are transmitted via HTTP/protobuf to http://collector.internal:4318 because generic endpoints override signal-specific variables
A Kubernetes deployment manifest for a microservice contains the following configuration: env:
- name: OTEL_SERVICE_NAME value: "order-checkout"
- name: OTEL_RESOURCE_ATTRIBUTES value: "service.name=legacy-cart,service.version=2.1.0,deployment.environment.name=production" When spans emitted by this service appear in the distributed tracing backend, what value is assigned to the service.name resource attribute, and why?
legacy-cart, because OTEL_RESOURCE_ATTRIBUTES is parsed after OTEL_SERVICE_NAME during SDK initialization
order-checkout-legacy-cart, because the OpenTelemetry SDK concatenates conflicting service naming variables with a hyphen
order-checkout, because the dedicated OTEL_SERVICE_NAME environment variable explicitly takes precedence over service.name defined in OTEL_RESOURCE_ATTRIBUTES
unknown_service:java, because conflicting service definitions invalidate the resource builder and trigger fallback naming
An operator raises OTEL_BSP_MAX_EXPORT_BATCH_SIZE to 4096 to reduce the number of export calls, but leaves OTEL_BSP_MAX_QUEUE_SIZE unset. What is wrong with this configuration?
Nothing; the batch size and the queue size are independent settings
The SDK automatically doubles the queue size whenever the batch size grows, so memory use will quadruple
OTEL_BSP_MAX_EXPORT_BATCH_SIZE applies only to the log record processor, so traces are unaffected
The batch size must not exceed the queue size, which defaults to 2048, so the value is invalid unless OTEL_BSP_MAX_QUEUE_SIZE is raised as well (for example to 8192)
A team sets OTEL_CONFIG_FILE to a YAML file that configures a parent_based sampler with an always_on root. The same container also sets OTEL_TRACES_SAMPLER=always_off, which the YAML file does not reference. Which sampler does the SDK use?
The parent_based sampler from the file, because every other OTEL_* variable is ignored when OTEL_CONFIG_FILE is set unless the file references it through ${...} substitution
always_off, because an explicit environment variable always overrides a configuration file
Both samplers, combined so that a span is recorded only when both of them agree
Neither sampler; the SDK refuses to start when a file and environment variables conflict
Sections you finish are checked off in the contents.