What Are OTEL_* Environment Variables in OpenTelemetry?

What Are OTEL_* Environment Variables in OpenTelemetry?

They are a standard set of named settings, all starting with the letters OTEL, that let you control how OpenTelemetry behaves without touching your application code. Change a value, restart the program, and your service name, destination, sampling, and many other behaviors change with it.

Imagine a television remote control. You do not open the television and rewire it each time you want to change the volume or the channel. You press a button, and the television reads your choice. OpenTelemetry works in a similar spirit. The instrumentation inside your application is the television, and the OTEL_* environment variables are the buttons on the remote. They let anyone who deploys the application, whether a developer, an operator, or a platform team, tune its telemetry behavior from the outside, using a common set of button names that every compatible tool understands.

1The Problem: Settings That Should Not Live in Code

Every telemetry system needs settings. The question is where those settings should live.

What needs to be configured

When an application produces traces, metrics, and logs, many decisions must be made. What is this service called? Where should the data be sent? Should every request be traced, or only a fraction? Which format should be used to pass trace identity to other services? How large can the internal buffers be? Each of these answers can differ from one environment to another.

A service running on a developer’s laptop may print telemetry to the console. The same service in a test environment may send everything to a small test backend. In production, it may sample only a small fraction of traces and send them to a central Collector with secure credentials. The code is identical in all three places, but the settings are different.

Why hard-coding settings is a bad idea

If these settings were written inside the source code, every change would need a code edit, a rebuild, and a new release. Teams would end up with different builds for different environments, which makes testing unreliable. Secrets such as access keys would be stored in source files and might leak. And if a service is built from a third-party product that you cannot edit, you would have no way to configure it at all.

Simple Analogy

Think of a restaurant recipe book. The recipe stays the same, but the number of guests, the spice level, and the serving plate change from table to table. A good kitchen keeps those choices on the order slip instead of reprinting the recipe book for every customer.

i
Key Idea

Environment variables separate what the program does from how it is set up. The program stays the same; the settings travel with the environment where it runs.

2A Quick Refresher on What an SDK Needs to Know

Before meeting the variables, it helps to list the decisions they control.

Decision 1

Identity

Who is producing this data? The service name, version, and environment label all belong here. These labels are called resource attributes.

Decision 2

Destination

Where should telemetry be sent, using which protocol, with which credentials, and how long should the program wait for an answer?

Decision 3

Volume

How much data should be kept? Sampling decides which traces are recorded, and limits decide how big individual records may grow.

Decision 4

Behavior

Which format passes trace identity between services, how often are batches sent, and how verbose should the SDK’s own messages be?

Decision 5

On and off

Should the SDK run at all, and which individual instrumentations should be enabled or disabled?

The idea of defaults

For nearly every setting, OpenTelemetry defines a sensible default. If you set nothing, the SDK uses those defaults. For example, a typical default is to send data using the OpenTelemetry Protocol to a local address. This is why a quick demo often works with almost no configuration, and why production setups then override only the values that must differ.

Simple Analogy

A new phone works the moment you switch it on, with default brightness, volume, and language. Then you open settings and change only what you care about. Defaults give you a working start, and your changes make it yours.

3So, What Are OTEL_* Environment Variables?

A plain definition first, then the two families you will meet in practice.

The definition

An environment variable is a named value that the operating system, a container platform, or a launcher script passes to a program when it starts. The program can read these values while it runs. The OTEL_* environment variables are those whose names start with the prefix OTEL_, and which OpenTelemetry defines as the standard way to configure its SDKs and instrumentation from the outside.

Why a shared prefix matters

The prefix tells everyone, humans and tools alike, that a variable belongs to OpenTelemetry. It prevents clashes with variables used by other software, and it makes the settings easy to find with a simple search of a deployment file. Most importantly, the names are specified in a language-neutral document. A service written in Java, one in Python, and one in JavaScript can all be configured with the same variable names, which greatly simplifies platform operations.

Two families of variables

FamilyWho defines itExample purposeWorks across languages?
Specification-defined variablesThe OpenTelemetry specificationService name, exporter endpoint, sampler choice, propagatorsYes, supported by compliant SDKs
Language-specific or tool-specific variablesThe individual language projects or agentsEnabling or disabling one library’s instrumentation, agent behaviorNo, each language has its own
“The same button names on every remote, whatever brand of television you own.”
!
Common Misconception

Not every OTEL_* variable works in every language or every version. Some are stable across the board, some are newer, and some only exist in one ecosystem. Always confirm support in the documentation of the SDK or agent you actually run.

i
Good to Know

The OpenTelemetry Collector, which is a separate program, is mostly configured through its own configuration file. It has its own way of reading environment values inside that file. The OTEL_* variables discussed here mainly configure SDKs and instrumentation inside applications.

4How Environment Variables Travel and How Values Are Read

A little understanding of the mechanics prevents many beginner mistakes.

Where variables come from

Environment variables are set before a program starts and are inherited by the program from whatever launched it. On a laptop, that may be a terminal session or a script. In a container platform, they are usually declared in the deployment description, so every copy of the container starts with the same values. In managed cloud services, there is normally a settings page where you enter names and values.

They are read at startup

Most SDKs read these variables once, when the SDK is created. Changing a variable while the program is running usually has no effect until the program is restarted. This is why configuration changes are normally rolled out by restarting or redeploying the service.

Everything is text

An environment variable is always a piece of text. The SDK is responsible for converting it into the type that the setting needs. The specification defines clear rules for this conversion so that every SDK behaves consistently.

Type 1

Boolean

Only the word true, in any letter case, counts as true. Anything else, including values like yes or 1, is treated as false. An unset variable falls back to the default.

Type 2

Number

Counts, sizes, and durations are written as plain numbers. Durations are usually in milliseconds unless the documentation says otherwise for a specific setting.

Type 3

Enumerated choice

A fixed list of allowed words, such as the names of samplers or exporters. The words are matched without caring about upper or lower case.

Type 4

List

Several values written in one line, separated by commas, such as a list of propagator names.

Type 5

Key-value map

Pairs written as name equals value, separated by commas, such as the extra labels describing a service.

What happens when a value is wrong

The specification asks SDKs to be forgiving. If a value cannot be understood, the SDK should report a warning through its own internal logging and then ignore the bad value, using the default instead. It should not crash your application because of a typing mistake in a setting. The cost of this friendliness is that a typo can silently leave you with the default behavior, so it is wise to look for warnings during testing.

!
Easy to Miss

Writing a boolean setting as yes, on, or 1 does not turn it on. Only the word true does. Because wrong values fall back to defaults, you may never see an error message, only unexpected behavior.

Simple Analogy

It is like a form that a clerk reads aloud. If you write something unreadable in a box, the polite clerk skips that box and uses the standard answer instead of refusing your whole form. Convenient, but you must check the receipt to see what was actually used.

5Identity Variables: Naming Your Service

If you set only two things, make them the service name and its descriptive labels.

OTEL_SERVICE_NAME

This variable sets the logical name of your service, such as “checkout” or “inventory.” The name appears on every trace, metric, and log produced by the service, and it is the main way tools group and display data. Without it, SDKs fall back to an unhelpful default, which makes data from different services indistinguishable.

OTEL_RESOURCE_ATTRIBUTES

This variable carries extra descriptive labels for the service, written as a comma-separated list of name-and-value pairs. Common labels include the service version, the deployment environment such as production or staging, the owning team, and the cloud region. These labels are called resource attributes because they describe the resource that produces the telemetry.

How the two interact

The service name can also be written inside the resource attributes list. If both are present, the dedicated service name variable takes precedence. This lets operators override the name easily without editing a long list of attributes.

VariableWhat it holdsTypical example in wordsWhy it matters
OTEL_SERVICE_NAMEOne nameThe word checkoutGroups all signals by service
OTEL_RESOURCE_ATTRIBUTESSeveral labels as name equals value pairsVersion, environment, teamLets you filter and compare by release, environment, or owner

Special characters in values

Because the list uses commas and equals signs as separators, values that contain those characters need to be encoded, using the percent-encoding rules described in the specification. This is an easy place for subtle mistakes, especially when values are built automatically by scripts or platform templates.

Example: Labeling a release

A team deploys version 2.4 of the checkout service to production. Through the resource attributes variable, they attach the version and environment labels. When latency rises, engineers can compare telemetry from version 2.3 and version 2.4 in one query, and immediately see that the new release is responsible.

i
Practical Tip

Container platforms can pass dynamic facts, such as the pod name or node name, into environment variables. You can then include them in the resource attributes so that every signal carries the exact instance that produced it.

Simple Analogy

The service name is the name on a parcel, and the resource attributes are the extra stickers: fragile, express, handle with care. Both travel with the parcel, and every sorting office reads them.

6Exporter Variables: Where the Data Goes

The largest family of OTEL_* variables controls how telemetry leaves your application.

The OTLP exporter family

OTLP, the OpenTelemetry Protocol, is the standard way of sending telemetry. Its settings all begin with OTEL_EXPORTER_OTLP. They control the address, the transport protocol, the credentials, the compression, and the waiting time.

Setting 1

OTEL_EXPORTER_OTLP_ENDPOINT

The base address of the receiver, usually a Collector. Typical defaults point to the local machine, with one standard port for the gRPC transport and another for the HTTP transport.

Setting 2

OTEL_EXPORTER_OTLP_PROTOCOL

The transport style. Common choices are gRPC, HTTP with a binary body, and HTTP with a text body. Defaults can differ between languages, so set this explicitly when it matters.

Setting 3

OTEL_EXPORTER_OTLP_HEADERS

Extra headers sent with each request, written as name and value pairs. This is typically where an access token or an API key is passed to a hosted backend.

Setting 4

OTEL_EXPORTER_OTLP_COMPRESSION

Whether to compress the data in transit. Compression saves network bandwidth at the cost of a little processing time.

Setting 5

OTEL_EXPORTER_OTLP_TIMEOUT

The maximum time to wait for the receiver to respond to one export attempt before giving up on it.

Setting 6

Certificate and security settings

Variables that point to trusted certificates, client certificates, and client keys, or that allow insecure connections for testing.

General versus signal-specific

Most exporter variables exist in two forms. The general form applies to all signals, and a signal-specific form applies only to traces, only to metrics, or only to logs. When both are set, the more specific one wins for its signal. This lets you send traces to one place and metrics to another, while keeping the shared settings, such as credentials, in one variable.

flowchart TD
    START["Exporter needs a setting for traces"] --> Q1{"Is the traces specific variable set?"}
    Q1 -- "Yes" --> USE1["Use the traces specific value"]
    Q1 -- "No" --> Q2{"Is the general OTLP variable set?"}
    Q2 -- "Yes" --> USE2["Use the general value"]
    Q2 -- "No" --> USE3["Use the built in default"]
        
FIG 1 — Signal-specific values override general ones, which override defaults

A subtle detail about endpoint addresses

For HTTP-based transport, the general endpoint variable is treated as a base address, and the SDK appends a standard path for each signal, such as one for traces, one for metrics, and one for logs. A signal-specific endpoint variable, however, is used exactly as written, with no path added. This difference surprises many people, and it is a common reason that data arrives at the wrong path of a receiver.

!
Common Mistake

Putting a full signal path in the general endpoint variable, or leaving the path out of a signal-specific one. Either way the receiver may reject the data. Remember: general means base address, specific means final address.

Simple Analogy

A general instruction says “deliver all parcels to the main post office.” A specific instruction for fragile parcels says “deliver these to counter number five.” The counter instruction wins for fragile parcels, and the general one covers everything else.

7Choosing Exporters Per Signal

Besides configuring the exporter, you can choose which exporter each signal uses, or turn export off.

The three selection variables

Three variables pick the exporter for each signal: one for traces, one for metrics, and one for logs. Each accepts a choice such as OTLP, a console output for debugging, or none to switch that signal’s export off. Some signals support additional choices, for instance a pull-based format for metrics that monitoring tools can scrape, or an older tracing format that some systems still use.

VariableSignalCommon choicesPurpose of the choices
OTEL_TRACES_EXPORTERTracesotlp, console, none, and some legacy formatsSend to a Collector, print for debugging, or disable
OTEL_METRICS_EXPORTERMetricsotlp, prometheus, console, nonePush to a Collector, expose for scraping, print, or disable
OTEL_LOGS_EXPORTERLogsotlp, console, noneSend to a Collector, print, or disable

Using none and console wisely

The none choice is helpful when you want traces but not metrics, or when you want to test an application without producing any outbound traffic. The console choice is helpful during development, because telemetry appears right in the terminal where you can read it. Neither is intended for production use in most cases, since console output is hard to analyze and none discards data.

Multiple exporters

Some SDKs accept a list, such as sending to the Collector and also printing to the console at the same time. Support for lists varies by language, so test the behavior in your environment before relying on it.

Example: Debugging a missing trace

A developer suspects that spans are not being created at all. They temporarily set the traces exporter to console and restart the service. Spans appear in the terminal, proving that creation works and that the problem lies in the network path to the Collector.

i
Debugging Habit

When telemetry does not show up in your backend, switch to the console exporter first. It splits the problem in half: either data is not produced, or it is produced but not delivered.

Simple Analogy

When a radio seems silent, you first check whether the broadcaster is talking by plugging in headphones directly. If you hear sound, the problem is the antenna, not the station.

8Sampling and Propagation Variables

Two small groups of variables shape how many traces you keep and how trace identity travels between services.

Sampling

Sampling is the practice of keeping only a portion of traces to control cost. The sampler variable chooses the strategy by name, and a companion argument variable supplies a value for strategies that need one, such as a ratio.

Sampler 1

always_on and always_off

Keep every trace, or keep none. Useful for development, or for fully switching tracing off while leaving other signals alone.

Sampler 2

traceidratio

Keep a fixed fraction of traces, decided from the trace identifier. The fraction is given through the argument variable.

Sampler 3

parentbased variants

Follow the decision already made by the calling service, and apply a chosen strategy only for requests that start here. This keeps traces whole across services.

Sampler 4

Remote-controlled samplers

Some samplers fetch their rules from a central service, so sampling can change without redeploying. Availability depends on the language and ecosystem.

Why parent-based is the usual default

If every service made its own random decision, you would get traces that are recorded in one service and missing in the next. A parent-based sampler respects the choice made at the start of the request, so either the whole trace is kept or none of it. Many SDKs default to a parent-based sampler that keeps traces when the parent says so, and keeps new traces when there is no parent.

Propagators

The propagators variable lists the formats used to pass trace identity and baggage between services. The common default combines the W3C Trace Context standard with W3C Baggage. Other choices include older formats still used by some systems. All services in a call chain must share at least one format, or traces will break at the boundary.

VariablePurposeTypical value in wordsWarning
OTEL_TRACES_SAMPLERChoose the sampling strategyA parent-based ratio samplerLow ratios mean fewer traces to investigate
OTEL_TRACES_SAMPLER_ARGGive the strategy its parameterA number between zero and oneIgnored by samplers that take no argument
OTEL_PROPAGATORSChoose context formatsTrace context and baggageMismatches between services silently break traces
!
Consistency Is Key

Sampling and propagation settings should be consistent across your fleet. A service that uses a different propagation format than its neighbors will start new traces instead of joining existing ones.

Simple Analogy

Propagators are the language used on the baton in a relay race, and the sampler decides which races are filmed. If runners speak different languages, the baton is not recognized. If the cameras only film some legs, the film has gaps.

9Batching, Limits, and Metric Timing

Another group of variables tunes how much memory and time the SDK may use, and how often it sends data.

Batch processors

SDKs do not send every span or log the moment it finishes. Instead, a batch processor collects them in a queue and sends them in groups. Variables with the prefixes OTEL_BSP for spans and OTEL_BLRP for log records control the batching behavior.

Knob 1

Schedule delay

How long the processor waits between sending batches. Shorter delays make data appear sooner but create more network calls.

Knob 2

Maximum queue size

How many finished items can wait in memory. If the queue fills, new items are dropped, so this protects memory but may lose data under heavy load.

Knob 3

Maximum export batch size

The most items sent in one go. It must not exceed the queue size, and it balances efficiency against the size of each request.

Knob 4

Export timeout

How long one batch export may take before it is cancelled, so a slow receiver cannot block the application forever.

Size limits on telemetry

Other variables limit how large individual pieces of telemetry may grow. They cap the number of attributes per span, the number of events and links, and the length of attribute values. These limits protect the application from runaway memory use when some code attaches huge values, and they protect backends from oversized records.

Metric collection timing

Metrics are sent on a regular rhythm. A variable sets the interval between exports, and another sets the timeout for one export attempt. Variables for OTLP metrics also let you choose how counts are reported over time, such as cumulative totals versus changes since the last report, and how histograms are aggregated. These choices must match what your backend expects.

GroupExample variable prefixWhat it controlsRisk if set badly
Span batchingOTEL_BSPQueue size, delay, batch size, timeout for spansDropped spans if queue is too small, memory pressure if too large
Log batchingOTEL_BLRPThe same controls for log recordsLost logs during bursts
Attribute and span limitsOTEL_SPAN and OTEL_ATTRIBUTE limitsCounts and lengths of recorded detailsTruncated information if too low, bloat if too high
Metric timingOTEL_METRIC_EXPORTInterval and timeout for metric exportsStale dashboards if too slow, extra load if too fast
Metric temporality and aggregationOTEL_EXPORTER_OTLP_METRICS settingsHow values are reported and bucketedWrong charts if the backend expects the other style
i
Leave Defaults Until You Have a Reason

The default batching values suit most services. Tune them when you observe dropped data, memory pressure, or delays, and change one knob at a time so you can see its effect.

Simple Analogy

A mail van waits until it has enough parcels or until a set time passes, then leaves. A bigger van carries more per trip but needs more space. A shorter wait means faster delivery but more trips.

10Language-Specific and Agent Variables

Beyond the shared standard, each ecosystem adds its own settings for features unique to it.

Why language-specific variables exist

Some behavior only makes sense in one environment. A Java agent needs to be switched on or off and pointed at extra configuration. A Python launcher needs a way to disable particular instrumentations or to turn on log correlation. A .NET agent needs paths to its runtime components. These settings are not part of the common specification, so each project defines them, usually with an extra segment in the name that identifies the language or tool.

Family 1

Java agent settings

Variables whose names begin with OTEL_JAVAAGENT control the agent itself, and others turn individual library instrumentations on or off.

Family 2

Python settings

Variables beginning with OTEL_PYTHON control items such as disabled instrumentations and whether log records get trace identifiers.

Family 3

.NET settings

Variables beginning with OTEL_DOTNET configure automatic instrumentation, such as which components are enabled.

Family 4

JavaScript settings

Variables for Node.js control items such as resource detectors and which instrumentations are loaded.

Other cross-cutting variables

A few variables apply across languages but are specialized. One lets you disable the entire SDK, which is useful as an emergency off switch. One controls the verbosity of the SDK’s own internal messages. One manages the transition between older and newer naming conventions for attributes, allowing teams to opt in to the new names gradually. Newer parts of the specification also describe a way to point to a configuration file, as an alternative to many separate variables. Check the current status of that feature for your language.

!
Do Not Assume Portability

A variable that works in your Java service may be ignored by your Python service. Keep a per-language list of the variables you rely on, and test in each language rather than copying one setup everywhere.

Example: Turning off one noisy library

A team finds that automatic instrumentation of an internal caching library creates thousands of tiny spans. They use the agent’s per-library switch to disable only that instrumentation, leaving everything else running. The trace becomes readable, and the overhead drops.

Simple Analogy

All phones share a standard charging port, but each brand has its own special features in its own settings menu. The standard variables are the charging port; the language-specific ones are the brand menus.

11Precedence: Which Setting Wins?

The same setting may be given in several places. Knowing which one wins prevents confusing surprises.

The layers of configuration

A setting can come from several layers. At the bottom are the built-in defaults of the SDK. Above them are environment variables. Above those, in many setups, are values set explicitly in code, or in a configuration file, or through startup options of an agent. In general, the more explicit and closer to the program the setting is, the more likely it is to override a broader or more generic one. However, the exact rules differ between languages and tools, so confirm them in the documentation.

flowchart TD
    D["Built in defaults"] --> E["Environment variables"]
    E --> C["Configuration file or agent options, where supported"]
    C --> P["Values set explicitly in code"]
    P --> F["Final effective setting"]
        
FIG 2 — Typical layering from defaults up to explicit code settings

Within environment variables

Inside the family of environment variables themselves, two precedence rules are worth remembering. A signal-specific variable overrides the general one for that signal. And a dedicated variable overrides an equivalent value tucked inside a list, as with the service name versus the resource attributes list.

SituationWhich value winsWhy
General endpoint and traces-specific endpoint both setThe traces-specific one, for tracesSpecific overrides general
Service name in both the dedicated variable and the attributes listThe dedicated variableDedicated overrides embedded
Variable unset and code sets a valueThe code valueExplicit code is applied on top of defaults
Variable set and code sets a different valueUsually the code value, but it depends on the languageExplicit code is generally more specific
Variable has an invalid valueThe defaultInvalid values are ignored with a warning
i
A Healthy Habit

Decide, as a team, where each kind of setting lives. For example, keep deployment-specific values in environment variables and keep only truly fixed behavior in code. Mixing both for the same setting creates confusion about where to look.

Simple Analogy

It is like clothing rules: a company dress code is the default, a team memo overrides it, and your manager’s note for today’s client meeting overrides both. The most specific instruction near the moment of action usually wins.

12A Real-World Journey: Configuring a Service in Kubernetes

Let us see how these variables are used when a service is deployed to a cluster.

The scenario

A team runs a checkout service as several copies in a Kubernetes cluster. They want each copy to report under the name “checkout,” label its version and environment, send data to a Collector running on the same node, sample a tenth of new traces, and use the standard context format.

1

Declare the variables in the deployment

The deployment description lists the OTEL_* names and their values. Every copy of the container receives the same list when it starts.

2

Inject dynamic facts

The platform passes in facts known only at runtime, such as the pod name and node name, as extra variables. The resource attributes variable references them, so each copy labels itself uniquely.

3

The container starts and the SDK reads the variables

At startup, the SDK or agent reads each variable, converts the text into the right types, and applies the values on top of its defaults.

4

Telemetry flows with the right identity

Spans, metrics, and logs carry the service name and labels, are sampled at the chosen ratio, and are delivered to the Collector at the configured address.

5

A change is rolled out by redeploying

To change the sampling ratio, the team edits one value and rolls out the deployment. Containers restart, read the new value, and begin sampling differently, with no code change.

sequenceDiagram
    participant D as Deployment Description
    participant K as Kubernetes
    participant C as Container
    participant S as SDK or Agent
    participant O as Collector
    D->>K: List of OTEL variables and values
    K->>C: Start container with variables set
    C->>S: Application starts, SDK initializes
    S->>S: Read variables, convert, apply over defaults
    S->>O: Send telemetry with configured identity
    Note over S,O: Change a value, redeploy, and the cycle repeats
        
FIG 3 — From deployment description to configured telemetry
i
Automation Option

The OpenTelemetry Operator for Kubernetes can inject a standard set of variables for you when it attaches instrumentation to a workload. This helps platform teams apply the same defaults across many services consistently.

Example: A tenth of the traces in production, all in staging

The same container image is used in both environments. In staging, the sampler variable keeps every trace. In production, it keeps a tenth. Only the variable values differ, and no code or image is rebuilt.

13Security and Secrets

Environment variables are convenient, but they are not a safe vault. Some of them carry sensitive values.

Which variables may hold secrets

The headers variable for the OTLP exporter often contains an API key or a token for a hosted backend. Variables that point to private keys or certificates also deserve care. Even the resource attributes may reveal internal names or topology that you may not want to expose widely.

Where environment variables can leak

Leak 1

Process inspection

On many systems, anyone with enough access to the machine can view the environment of a running process.

Leak 2

Container and orchestrator views

Tools that describe running containers or deployments often display the variables in plain text to users with read access.

Leak 3

Version control and logs

Deployment files committed to repositories, or startup logs that print the environment, can expose values permanently.

Leak 4

Crash dumps and diagnostics

Support bundles and crash reports sometimes include the environment, which then travels to other people or systems.

Safer habits

Store secrets in the secret management feature of your platform and inject them into the environment only at runtime, instead of writing them in plain deployment files. Give each service the narrowest credentials possible, so a leak has a limited effect. Rotate keys periodically. Another approach is to send data to a local Collector without credentials, and let the Collector hold the backend secrets, so applications never see them.

!
Never Commit Secrets

Do not put access tokens directly into deployment files that are stored in shared repositories. Treat the headers variable as a secret if it contains any credentials.

Simple Analogy

Writing a key code on a sticky note on the front door is convenient, but everyone who walks by can read it. Keeping the key in a locked cabinet and handing it out only when needed is slightly more work and far safer.

14Strengths and Limits of Configuring by Environment

The environment variable approach is popular for good reasons, but it has real limits.

Advantages

  • No code changes or rebuilds to adjust behavior
  • One naming standard works across languages and tools
  • Fits containers and cloud platforms naturally
  • Easy to override per environment, per service, or per copy
  • Works even for third-party software you cannot edit
  • Sensible defaults give a working start with minimal effort

Disadvantages / Trade-offs

  • Values are plain text, with no validation until startup
  • Typos silently fall back to defaults
  • Complex structures, such as detailed pipelines, are awkward as flat variables
  • Changes usually require a restart
  • Support and defaults differ between languages and versions
  • Secrets in the environment can leak if handled carelessly

Where the limits show up

Complex pipelines

Describing several processors, different exporters per signal with distinct settings, and detailed views for metrics is hard with flat variables. This is part of the motivation for the file-based declarative configuration that the project is developing.

Many services with many variables

When dozens of services each carry a dozen variables, copies drift apart. Shared templates, platform-level defaults, or operator-based injection help keep the fleet consistent.

Runtime changes

If you need to change sampling during an incident without restarting, environment variables alone are not enough. Remote-controlled samplers or a Collector doing the sampling can help.

“Environment variables are the dial on the outside of the machine, not the control room inside it.”

15Troubleshooting, Best Practices, and Anti-patterns

When telemetry looks wrong, the cause is very often a setting. Here is a field guide.

Troubleshooting guide

SymptomLikely causeWhat to check
No data appears in the backendWrong endpoint, protocol mismatch, or exporter set to noneEndpoint address, protocol choice, exporter selection variables
Service shows an unknown or default nameService name variable missing or not readOTEL_SERVICE_NAME, and whether it is set in the container itself
Data arrives at the wrong path of the receiverGeneral versus specific endpoint confusionWhether the base address or a final address was used
Traces broken between servicesDifferent propagator formatsOTEL_PROPAGATORS in each service
A boolean setting seems ignoredValue was not the word trueThe exact spelling of boolean values
Setting works in one language but not anotherVariable is language-specific or newerThe documentation and version of each SDK
Change has no effectProgram not restarted, or code overrides the variableRestart behavior and explicit settings in code
Too many or too few tracesSampler or ratio misconfiguredOTEL_TRACES_SAMPLER and its argument

Habits that work

Practice 1

Always set the service name

Set it explicitly for every service, and keep naming consistent across environments.

Practice 2

Keep a shared baseline

Create a standard set of variables for all services, using templates or platform defaults, and let teams override only what they need.

Practice 3

Set the protocol explicitly

Do not rely on defaults that differ by language. Choose the protocol and endpoint on purpose.

Practice 4

Check startup warnings

Look at the SDK’s own messages during testing to catch invalid values that were silently replaced with defaults.

Practice 5

Protect secrets

Use your platform’s secret features and avoid placing credentials in files, logs, or screenshots.

Practice 6

Document your variables

Record which variables your fleet uses, their purpose, and which ones are language-specific, so new team members are not left guessing.

Traps to avoid

ANTI-PATTERN-01 Avoid
Problem

Every service is configured by hand with its own slightly different set of variables, copied from older services and edited over time.

Why It’s Harmful

The fleet drifts. Some services use different propagators, names, or endpoints, and traces and dashboards become inconsistent.

Correct Approach

Define a common baseline in a shared template or platform layer, and allow only deliberate, documented overrides.

ANTI-PATTERN-02 Avoid
Problem

Access tokens are written directly into deployment files in plain text and stored in a shared repository.

Why It’s Harmful

Anyone with repository access can read the token, and it remains in history even after removal.

Correct Approach

Use the secret management feature of your platform, inject values at runtime, and rotate tokens regularly.

ANTI-PATTERN-03 Avoid
Problem

The same setting is defined in environment variables, in code, and in an agent configuration file, with different values.

Why It’s Harmful

Nobody knows which one is effective. Changes appear to have no effect, and debugging becomes guesswork.

Correct Approach

Choose one primary place per setting, document it, and remove duplicates.

ANTI-PATTERN-04 Avoid
Problem

Production runs with full sampling and default queue sizes copied from a demo setup, with no review of volume or cost.

Why It’s Harmful

Telemetry volume and overhead can be far higher than needed, and queues may overflow during bursts, causing both cost and data loss.

Correct Approach

Choose sampling and batching values deliberately for production, test under realistic load, and revisit them as traffic changes.

16Frequently Asked Questions

Quick answers to the questions people ask most often.

Q1Do I need to set any OTEL_* variables at all?

Not to get started, because defaults exist. But in practice you should always set the service name, and usually the exporter endpoint and the protocol, so your data is labeled and delivered correctly.

Q2Are the variable names the same in every language?

The specification-defined variables are meant to be the same across languages that support them. Language-specific variables, such as those for agents and launchers, differ by ecosystem. Always check the documentation for your language and version.

Q3Can I change a variable while the application is running?

Usually not. Most SDKs read variables once at startup, so changes take effect after a restart. For live changes, consider mechanisms such as remote-controlled samplers or processing in a Collector.

Q4What happens if I make a typo in a value?

The SDK is supposed to log a warning and use the default value instead. That means your application keeps running, but with the default behavior, so you should check startup messages when testing new settings.

Q5Why does my boolean variable not turn the feature on?

Because only the word true, in any letter case, is read as true. Values such as yes, on, or one are read as false. Use the exact word true.

Q6What is the difference between the general and signal-specific OTLP variables?

The general variables apply to all signals. The signal-specific ones apply only to traces, metrics, or logs, and they override the general ones for that signal. For HTTP endpoints, the general one is a base address, while the specific one is used exactly as written.

Q7Do OTEL_* variables configure the Collector?

Mostly no. The Collector is configured by its own file, which has its own way to insert environment values. The OTEL_* variables described here mainly configure SDKs and instrumentation inside applications.

Q8Which setting wins if I set something in both code and an environment variable?

Often the setting in code takes precedence, because it is more explicit, but the exact behavior depends on the language and the tool. Avoid defining the same setting in both places to prevent confusion.

Q9Is it safe to put an API key in the headers variable?

It is common, but treat it as a secret. Inject it through your platform’s secret management, limit who can view the environment, and rotate it regularly. Alternatively, let a Collector hold the credentials.

Q10Is there an alternative to many separate variables?

The project is developing a declarative configuration approach, where a file describes the full setup. Support and maturity vary by language, so check the current status before adopting it.

17Summary and Key Takeaways

The OTEL_* environment variables are OpenTelemetry’s standard remote control. They let operators and developers set a service’s name and labels, choose exporters and their addresses, select sampling and propagation behavior, tune batching and limits, and enable or disable features, all without changing source code. Their names are shared across languages, their values are plain text that SDKs convert using clear rules, and invalid values quietly fall back to defaults. Specific settings override general ones, and explicit code often overrides environment values, so it is wise to decide where each setting lives. Language-specific variables extend the standard for particular agents. Used with care for secrets, consistency across the fleet, and attention to startup warnings, they make telemetry configuration simple, repeatable, and portable.

Key Takeaways

  • Standard names, outside the code — OTEL_* variables configure SDKs and instrumentation through the environment, so behavior changes without code edits.
  • Two families — specification-defined variables work across compliant languages, while language-specific variables belong to individual agents and ecosystems.
  • Always set identity — the service name and resource attributes label every signal and make data usable.
  • Specific beats general — signal-specific exporter variables override general ones, and for HTTP the general endpoint is a base address while the specific one is used as written.
  • Values are text with forgiving rules — only the word true counts as true, enumerations ignore letter case, and bad values fall back to defaults with a warning.
  • Tune sampling, propagation, and batching on purpose — consistent propagators and a deliberate sampling choice keep traces whole and costs under control.
  • Protect and standardize — treat headers and keys as secrets, keep a shared baseline across services, and check precedence when a change seems to have no effect.