Skip to content

Environment variables

Who actually reads these

Not this module. otelcore, tracing, metrics and logs contain no os.Getenv call. Every variable below is read by the OpenTelemetry SDK or by the OTLP/HTTP exporters underneath, and the only thing this module decides is which SDK options it passes on top — because an explicitly-passed option always wins over an environment variable.

That single rule explains the whole page. A variable works when this module passes no option for that setting, and is ignored when it does.

Everything here was read from otel/sdk v1.44.0, the OTLP/HTTP exporters at v1.44.0 (sdk/log v0.20.0), and confirmed by exporting to a real collector.

Choosing the target: OTEL_EXPORTER_OTLP_ENDPOINT

Variable Applies when
OTEL_EXPORTER_OTLP_ENDPOINT Settings.Endpoint is ""
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Settings.Endpoint is "" (traces only; wins over the generic form)
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT Settings.Endpoint is "" (metrics only)
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT Settings.Endpoint is "" (logs only)

A non-empty Settings.Endpoint makes this module pass WithEndpoint and WithURLPath, and the variables are then ignored. There is no way to make configuration and environment cooperate on the endpoint: one or the other decides.

The signal-specific form takes the URL whole, including the path. The generic form takes a base and the SDK appends /v1/traces, /v1/metrics or /v1/logs — the same thing this module does with a configured endpoint.

If neither is set and Settings.Endpoint is empty, the exporter does not do nothing. It falls back to the SDK default of localhost:4318, and every export attempt fails against a machine with no collector on it. An empty endpoint means "somebody else will say where", never "disabled".

Variables that still apply when you configure an endpoint

These change settings this module passes no option for, so they work on both paths — the env-fallback path and a configured Settings.Endpoint. Each also has a signal-specific form (OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, and so on).

Variable Effect Default
OTEL_EXPORTER_OTLP_TIMEOUT Per-request timeout, in milliseconds 10000
OTEL_EXPORTER_OTLP_COMPRESSION gzip or none none
OTEL_EXPORTER_OTLP_CERTIFICATE PEM file of trusted CA certificates system roots
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE Client certificate for mTLS unset
OTEL_EXPORTER_OTLP_CLIENT_KEY Client key for mTLS unset
HTTPS_PROXY / HTTP_PROXY / NO_PROXY Standard Go proxy resolution unset

The TLS variables are the only route to a private CA or to mTLS — this module exposes no WithTLSClientConfig equivalent.

Combining OTEL_EXPORTER_OTLP_CERTIFICATE with an insecure endpoint is rejected at construction time: the exporter refuses a TLS config on a plaintext connection, and NewProvider returns creating OTLP … exporter. See Errors.

OTEL_EXPORTER_OTLP_HEADERS replaces, or is replaced — it never merges

Situation Result
Settings.Headers is empty and the variable is set The variable's headers are sent
Settings.Headers is non-empty The configured map is sent and the variable is discarded entirely

This module passes WithHeaders(s.Headers) whenever the map is non-empty, on both the configured-endpoint and env-fallback paths, and the SDK's WithHeaders assigns the map rather than merging into it. So a service with a single telemetry.headers entry loses every header an operator set in the environment — including on the env-fallback path, where the endpoint is coming from the environment and the headers are not.

If you need both, merge them in your own configuration layer before building Settings.

OTEL_EXPORTER_OTLP_INSECURE can downgrade a configured https:// endpoint

This one is worth stating plainly, because it runs against what the rest of the module does. otelcore.ParseEndpoint validates the scheme and rejects anything but http and https — and then OTEL_EXPORTER_OTLP_INSECURE=true can still make an https:// endpoint export in plaintext.

The mechanism: the SDK applies environment configuration first and options second, and this module only ever adds WithInsecure(). It never adds the SDK's WithSecure(), so there is nothing to undo an insecure flag that arrived from the environment.

Settings.Endpoint Settings.Insecure Environment Wire protocol
https://host:4318 false TLS
https://host:4318 false OTEL_EXPORTER_OTLP_INSECURE=true plaintext
https://host:4318 false OTEL_EXPORTER_OTLP_ENDPOINT=http://anything plaintext
https://host:4318 true plaintext
http://host:4318 false plaintext

The third row is the surprising one. A configured endpoint means the environment's host is ignored — but the SDK derives an insecure flag from the environment endpoint's scheme before this module's options are applied, and that flag survives. So a stale OTEL_EXPORTER_OTLP_ENDPOINT=http://… left over from a local development setup downgrades production traffic to a host it does not even point at.

Treat both variables as deployment-level switches that outrank your configuration, and do not rely on an https:// endpoint alone to guarantee TLS.

OTEL_RESOURCE_ATTRIBUTES is merged underneath your resource

otelcore.Resource(name, version) builds a resource carrying exactly service.name and service.version. All three SDK providers then merge that over resource.Environment(), so OTEL_RESOURCE_ATTRIBUTES does reach the exported resource — for anything that does not collide.

export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod,service.instance.id=pod-7
Resource attributes:
     -> deployment.environment: Str(prod)
     -> service.instance.id: Str(pod-7)
     -> service.name: Str(checkout)
     -> service.version: Str(0.1.0)

OTEL_SERVICE_NAME, and a service.name inside OTEL_RESOURCE_ATTRIBUTES, are the exception: otelcore.Resource always sets service.name, and the explicit resource wins the merge. The service name is a code-level decision here, not an operator-level one.

What you will not see is anything from resource.Default() — no telemetry.sdk.name, telemetry.sdk.language or telemetry.sdk.version, and no host or process attributes. otelcore.Resource uses resource.NewWithAttributes, which runs no detectors. If your backend groups by service.instance.id or host.name, supply them through OTEL_RESOURCE_ATTRIBUTES.

Variables this module always overrides

Setting any of these changes nothing, because this module unconditionally passes the corresponding SDK option — even when you supply no functional option, since the option carries the package default.

Variable Overridden by Use instead
OTEL_TRACES_SAMPLER sdktrace.WithSampler(...), always passed tracing.WithSampling(ratio)
OTEL_TRACES_SAMPLER_ARG as above tracing.WithSampling(ratio)
OTEL_METRIC_EXPORT_INTERVAL sdkmetric.WithInterval(...), always passed metrics.WithInterval(d)

OTEL_METRIC_EXPORT_TIMEOUT is not in this table — this module passes no timeout option, so that one works.

If you need an operator to be able to change the sampling ratio or the export interval without a rebuild, read the value into your own configuration and pass it to the option.

Batching and limits

Passed through untouched, because this module configures none of them.

Variable group Applies to Documented in
OTEL_BSP_* Span batch processor Provider options
OTEL_BLRP_* Log-record batch processor Provider options
OTEL_METRIC_EXPORT_TIMEOUT Periodic metric reader Provider options
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, OTEL_SPAN_EVENT_COUNT_LIMIT, OTEL_SPAN_LINK_COUNT_LIMIT, OTEL_ATTRIBUTE_COUNT_LIMIT, OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT Span limits OpenTelemetry specification
OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT, OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT Log-record limits OpenTelemetry specification
OTEL_METRICS_EXEMPLAR_FILTER Whether metric exemplars record trace context OpenTelemetry specification

OTEL_SDK_DISABLED is not honoured. It is read by the SDK's own auto-configuration package, which this module does not use — building a provider always builds a working one. Use Settings.Enabled and skip the constructor.