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.
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.
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.
Identity
Who is producing this data? The service name, version, and environment label all belong here. These labels are called resource attributes.
Destination
Where should telemetry be sent, using which protocol, with which credentials, and how long should the program wait for an answer?
Volume
How much data should be kept? Sampling decides which traces are recorded, and limits decide how big individual records may grow.
Behavior
Which format passes trace identity between services, how often are batches sent, and how verbose should the SDK’s own messages be?
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.
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
| Family | Who defines it | Example purpose | Works across languages? |
|---|---|---|---|
| Specification-defined variables | The OpenTelemetry specification | Service name, exporter endpoint, sampler choice, propagators | Yes, supported by compliant SDKs |
| Language-specific or tool-specific variables | The individual language projects or agents | Enabling or disabling one library’s instrumentation, agent behavior | No, each language has its own |
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.
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.
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.
Number
Counts, sizes, and durations are written as plain numbers. Durations are usually in milliseconds unless the documentation says otherwise for a specific setting.
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.
List
Several values written in one line, separated by commas, such as a list of propagator names.
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.
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.
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.
| Variable | What it holds | Typical example in words | Why it matters |
|---|---|---|---|
| OTEL_SERVICE_NAME | One name | The word checkout | Groups all signals by service |
| OTEL_RESOURCE_ATTRIBUTES | Several labels as name equals value pairs | Version, environment, team | Lets 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.
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.
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.
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.
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.
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.
OTEL_EXPORTER_OTLP_COMPRESSION
Whether to compress the data in transit. Compression saves network bandwidth at the cost of a little processing time.
OTEL_EXPORTER_OTLP_TIMEOUT
The maximum time to wait for the receiver to respond to one export attempt before giving up on it.
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"]
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.
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.
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.
| Variable | Signal | Common choices | Purpose of the choices |
|---|---|---|---|
| OTEL_TRACES_EXPORTER | Traces | otlp, console, none, and some legacy formats | Send to a Collector, print for debugging, or disable |
| OTEL_METRICS_EXPORTER | Metrics | otlp, prometheus, console, none | Push to a Collector, expose for scraping, print, or disable |
| OTEL_LOGS_EXPORTER | Logs | otlp, console, none | Send 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.
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.
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.
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.
traceidratio
Keep a fixed fraction of traces, decided from the trace identifier. The fraction is given through the argument variable.
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.
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.
| Variable | Purpose | Typical value in words | Warning |
|---|---|---|---|
| OTEL_TRACES_SAMPLER | Choose the sampling strategy | A parent-based ratio sampler | Low ratios mean fewer traces to investigate |
| OTEL_TRACES_SAMPLER_ARG | Give the strategy its parameter | A number between zero and one | Ignored by samplers that take no argument |
| OTEL_PROPAGATORS | Choose context formats | Trace context and baggage | Mismatches between services silently break traces |
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.
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.
Schedule delay
How long the processor waits between sending batches. Shorter delays make data appear sooner but create more network calls.
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.
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.
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.
| Group | Example variable prefix | What it controls | Risk if set badly |
|---|---|---|---|
| Span batching | OTEL_BSP | Queue size, delay, batch size, timeout for spans | Dropped spans if queue is too small, memory pressure if too large |
| Log batching | OTEL_BLRP | The same controls for log records | Lost logs during bursts |
| Attribute and span limits | OTEL_SPAN and OTEL_ATTRIBUTE limits | Counts and lengths of recorded details | Truncated information if too low, bloat if too high |
| Metric timing | OTEL_METRIC_EXPORT | Interval and timeout for metric exports | Stale dashboards if too slow, extra load if too fast |
| Metric temporality and aggregation | OTEL_EXPORTER_OTLP_METRICS settings | How values are reported and bucketed | Wrong charts if the backend expects the other style |
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.
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.
Java agent settings
Variables whose names begin with OTEL_JAVAAGENT control the agent itself, and others turn individual library instrumentations on or off.
Python settings
Variables beginning with OTEL_PYTHON control items such as disabled instrumentations and whether log records get trace identifiers.
.NET settings
Variables beginning with OTEL_DOTNET configure automatic instrumentation, such as which components are enabled.
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.
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.
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"]
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.
| Situation | Which value wins | Why |
|---|---|---|
| General endpoint and traces-specific endpoint both set | The traces-specific one, for traces | Specific overrides general |
| Service name in both the dedicated variable and the attributes list | The dedicated variable | Dedicated overrides embedded |
| Variable unset and code sets a value | The code value | Explicit code is applied on top of defaults |
| Variable set and code sets a different value | Usually the code value, but it depends on the language | Explicit code is generally more specific |
| Variable has an invalid value | The default | Invalid values are ignored with a warning |
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.
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.
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.
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.
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.
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.
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
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
Process inspection
On many systems, anyone with enough access to the machine can view the environment of a running process.
Container and orchestrator views
Tools that describe running containers or deployments often display the variables in plain text to users with read access.
Version control and logs
Deployment files committed to repositories, or startup logs that print the environment, can expose values permanently.
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.
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.
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.
15Troubleshooting, Best Practices, and Anti-patterns
When telemetry looks wrong, the cause is very often a setting. Here is a field guide.
Troubleshooting guide
| Symptom | Likely cause | What to check |
|---|---|---|
| No data appears in the backend | Wrong endpoint, protocol mismatch, or exporter set to none | Endpoint address, protocol choice, exporter selection variables |
| Service shows an unknown or default name | Service name variable missing or not read | OTEL_SERVICE_NAME, and whether it is set in the container itself |
| Data arrives at the wrong path of the receiver | General versus specific endpoint confusion | Whether the base address or a final address was used |
| Traces broken between services | Different propagator formats | OTEL_PROPAGATORS in each service |
| A boolean setting seems ignored | Value was not the word true | The exact spelling of boolean values |
| Setting works in one language but not another | Variable is language-specific or newer | The documentation and version of each SDK |
| Change has no effect | Program not restarted, or code overrides the variable | Restart behavior and explicit settings in code |
| Too many or too few traces | Sampler or ratio misconfigured | OTEL_TRACES_SAMPLER and its argument |
Habits that work
Always set the service name
Set it explicitly for every service, and keep naming consistent across environments.
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.
Set the protocol explicitly
Do not rely on defaults that differ by language. Choose the protocol and endpoint on purpose.
Check startup warnings
Look at the SDK’s own messages during testing to catch invalid values that were silently replaced with defaults.
Protect secrets
Use your platform’s secret features and avoid placing credentials in files, logs, or screenshots.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.