What Are Resource Detectors in OpenTelemetry?
A friendly, ground-up guide to the small helpers that discover where your software is running, what it is called and who it belongs to, and stamp that identity on every trace, metric and log, without you typing it all by hand.
Imagine you find a lost suitcase at an airport. You open it and look for a luggage tag. If there is a tag with a name, a flight number and a phone number, you can return the suitcase in minutes. If there is no tag, the suitcase might sit in a storage room for months, and nobody will know whose it is, where it came from or where it was going.
Telemetry, which is the information that software sends out about itself, has exactly this problem. A single span that says “this call took 900 milliseconds” is not very helpful if you cannot tell which service made it, which machine it ran on, which version of the code was running, or which cloud region it came from. In OpenTelemetry, the luggage tag is called a resource. And the helpers that fill in the luggage tag automatically, by looking around at the environment, are called resource detectors. In this guide, we will learn what they are, how they work, and how to use them wisely, using simple words and everyday examples.
1The Problem: Whose Data Is This?
Before learning the tool, let us feel the problem it solves.
A busy company with many services
Picture an online food delivery company. It runs a hundred different services: one for the menu, one for orders, one for payments, one for finding drivers, and many more. Each service runs in many copies, called instances, spread over many machines and several cloud regions. When the telemetry from all these copies arrives in one place, millions of spans, numbers and log lines arrive together.
Now an engineer asks a simple question: “Why is the payment service slow in the Mumbai region since this morning’s release?” To answer, the data must carry certain facts. It must say which service produced it, which version of that service, which region, and ideally which machine or pod. Without these facts, the engineer sees a giant pile of anonymous numbers.
The pain of typing it all by hand
One answer is to ask every developer to add these facts by hand in every service. But that has serious problems.
- People forget. One team forgets the region, another spells the version differently, and the data becomes inconsistent.
- Facts change. The same code may run on a laptop today and in a container tomorrow, and the machine name changes every time it is deployed.
- Some facts are hard to know. Does your code know the ID of the cloud virtual machine it is running on? The name of the Kubernetes pod? The ID of the container? These facts live in the environment around the program, not in the program itself.
- It is repetitive. Two hundred services would each need the same code to look up the same facts.
Think of a hotel check-in desk. Each guest gets a wristband with their name, room number and stay dates. The hotel does not ask every guest to write their own wristband. A staff member looks up the booking and prints it. Resource detectors are the staff member who prints the wristband for your software, using facts they can look up around them.
flowchart LR
subgraph NO["Without resource information"]
A1["Span: slow call, 900 ms"] --> A2["Which service? Unknown"]
A2 --> A3["Which region? Unknown"]
A3 --> A4["Which version? Unknown"]
end
subgraph YES["With resource information"]
B1["Span: slow call, 900 ms"] --> B2["Service: payments"]
B2 --> B3["Region: ap-south-1"]
B3 --> B4["Version: 2.4.1"]
end
2First, What Is a Resource?
We must understand the luggage tag before we can understand the person who fills it in.
In OpenTelemetry, a resource is a set of facts that describes the thing that produces telemetry. That thing might be a service, a process, a container, a machine, or a function running in the cloud. The facts are stored as attributes, which are simple pairs of a name and a value, like “service.name equals payments” or “cloud.region equals ap-south-1”.
Three important properties
Describes the Producer
A resource says who is sending the data, not what the data is about. It describes the sender, not the single event.
Attached to Everything
The same resource is attached to every span, every metric and every log record that the application sends, so you do not repeat it for each item.
Immutable
Once the resource is created, it does not change for the life of that SDK. It is a stable identity, not a changing status.
Resource versus span attributes
Beginners often confuse these two, so let us separate them with a table.
| Question | Resource Attribute | Span Attribute |
|---|---|---|
| What does it describe? | The sender: the service, host, cloud or container | One single operation, such as one request |
| How often does it change? | Rarely, usually fixed for the process lifetime | Different for every span |
| How is it stored? | Once per batch of telemetry | On each span individually |
| Example | service.name, cloud.region | http.request.method, a customer tier |
| Everyday picture | The luggage tag | A note about what is inside this one bag |
A letter has two kinds of information. The return address on the envelope says who sent it and does not change from page to page. The contents of each page say something specific. The resource is the return address on the envelope. The span attributes are the words on the pages.
3So, What Are Resource Detectors?
The short answer first, and then the details.
A resource detector is a small component that automatically discovers facts about the environment where your software is running, and turns those facts into resource attributes. It looks at the machine, the operating system, the process, the container, the cloud provider and the orchestration system such as Kubernetes. Then it hands back a bundle of attributes, ready to be added to the resource.
What detectors can discover
Host and Operating System
The host name, the operating system type and version, and the processor type.
Process
The process ID, the program’s executable name, the runtime such as the language version, and who started it.
Container
The container ID and, in some cases, the container runtime and image name.
Cloud Provider
Which cloud, which account, which region and zone, and the ID of the virtual machine, task or function.
Kubernetes
The cluster, the namespace, the pod name, the node name and the deployment.
Environment Variables
Facts that you or your platform wrote down in standard settings, such as the service name.
What detectors are not
- They are not instrumentation. They do not create spans or metrics. They only describe the sender.
- They are not magic. They can only discover what the environment makes available. If the environment hides a fact, the detector cannot find it.
- They do not describe individual requests. For per-request facts, you use span attributes.
- They are not the same as the Kubernetes enrichment tool in the Collector. We will explain this difference carefully in a later chapter, because it is a common source of confusion.
You will meet resource detectors in two places: inside the SDK of your application, and inside the Collector as a processor called the resource detection processor. They share the same idea but work in different places. This guide covers both.
4Core Concepts: The Families of Resource Attributes
OpenTelemetry has agreed names for common facts, grouped into families. Knowing the families helps you read any trace.
The agreed names come from the semantic conventions, which are shared naming rules for telemetry. Using the same names everywhere means that dashboards, alerts and tools from different vendors understand your data without extra work. Here are the main families.
| Family | Example Attribute Names | What They Tell You |
|---|---|---|
| Service | service.name, service.version, service.namespace, service.instance.id | Which logical service, which release, which team group and which individual copy |
| Telemetry SDK | telemetry.sdk.name, telemetry.sdk.language, telemetry.sdk.version | Which OpenTelemetry library produced the data |
| Host | host.name, host.id, host.arch | The machine, physical or virtual |
| Operating System | os.type, os.description | The operating system family and version |
| Process | process.pid, process.executable.name, process.runtime.name | The running program and its language runtime |
| Container | container.id, container.name, container.image.name | The container the program runs in |
| Cloud | cloud.provider, cloud.account.id, cloud.region, cloud.availability_zone | Which cloud, account, region and zone |
| Kubernetes | k8s.cluster.name, k8s.namespace.name, k8s.pod.name, k8s.node.name | Where the workload sits in the cluster |
| Deployment | deployment.environment | Whether this is production, staging or testing |
The most important attribute of all
If you remember only one resource attribute, remember service.name. It is the logical name of your service, such as “checkout” or “payments”. Almost every tracing and monitoring tool uses it as the main way to group data. If you do not set it, the SDK uses a placeholder that begins with the words “unknown service” followed by the name of the running program. Seeing “unknown service” in your tools is the classic sign that nobody set the service name.
Think of a school ID card. The student’s name is the most important line, and the class, the house and the year are extra lines that help sort the students. If the name were missing, the card would almost be useless, however many other lines it had. service.name is the name line of your telemetry.
Which ones can a detector find, and which must you provide?
This is a very practical question. Some facts are knowable from the environment, and some are choices that only your team can make.
Usually Detectable
- Host name, host ID and operating system
- Process ID, executable name and runtime
- Container ID
- Cloud provider, region, zone and instance ID
- Kubernetes pod, namespace and node, if passed into the container
- SDK name, language and version
Usually Must Be Provided
- The logical service name
- The service version, unless taken from the build
- The environment, such as production or staging
- The service namespace or team
- Any company-specific labels, such as cost centre
5Internal Working: How Detection Happens in the SDK
Let us follow the story from the moment your program starts.
When your application starts and the OpenTelemetry SDK is set up, one of the first things it does is build the resource. It does this by running a list of detectors, one by one, collecting the facts each one finds, and then merging all the facts into one final resource. This resource is then handed to the providers for traces, metrics and logs, so every signal carries it.
sequenceDiagram
participant App as Application starts
participant SDK as OpenTelemetry SDK
participant D1 as Default detector
participant D2 as Environment detector
participant D3 as Host and process detectors
participant D4 as Cloud detector
participant TP as Tracer, Meter and Logger providers
App->>SDK: Initialise SDK
SDK->>D1: Ask for SDK facts and default service name
D1-->>SDK: telemetry.sdk and unknown service placeholder
SDK->>D2: Ask for facts from environment variables
D2-->>SDK: service.name and custom attributes
SDK->>D3: Ask for host, OS and process facts
D3-->>SDK: host.name, os.type, process.pid
SDK->>D4: Ask for cloud facts
D4-->>SDK: cloud.provider, cloud.region, host.id
SDK->>SDK: Merge all facts into one resource
SDK->>TP: Give the final resource to every provider
Start With a Default
The SDK begins with a basic resource that contains facts about the SDK itself and a placeholder service name that starts with the words “unknown service”.
Run Each Configured Detector
Each detector in the list looks at its own part of the environment and returns a small set of attributes. If a detector finds nothing, it returns an empty set, which is fine.
Merge the Results
The SDK combines the sets into one. When two detectors report the same attribute name, one of them wins, according to merge rules we will see shortly.
Freeze the Resource
The final resource is created and fixed. It will not change for the rest of this process’s life.
Attach to Every Signal
The tracer, meter and logger providers all use this same resource. Each batch of exported data carries it once, rather than repeating it for each span or data point.
Automatic and manual detection
In many setups, detection happens without you doing anything. A few detectors are always on, such as the one that records the SDK’s own facts. Others are enabled by settings. In auto-instrumentation setups, such as agents, a sensible group of detectors is often turned on for you. In manual setups, you pick the ones you want when you build the SDK.
Defaults differ between languages and versions. A detector that is on by default in one language may be off by default in another, especially cloud-specific ones. Always check what your own SDK or agent enables, and then look at a real piece of exported data to confirm what was actually detected.
6Merging and Precedence: Who Wins When Facts Disagree?
When several sources give an answer to the same question, there must be clear rules.
Imagine that the environment detector says the service is called “checkout”, while a cloud detector says nothing about the service name, and the default detector says “unknown service”. All three provide a value, or a placeholder, for the same attribute. The SDK needs a rule for which one to keep.
The general rule is simple: when two resources are merged, the values from the later or more specific one override the earlier one on conflicts. The default placeholders sit at the bottom, and explicit choices made by you sit at the top. In most SDKs, values you set through the standard environment variables take priority over automatically detected ones.
flowchart TD
A["Lowest priority: SDK defaults and placeholders"] --> B["Automatic detectors: host, OS, process, container, cloud"]
B --> C["Environment variable detector: OTEL_RESOURCE_ATTRIBUTES"]
C --> D["Highest priority: OTEL_SERVICE_NAME and values set in code"]
D --> E["Final resource"]
| Situation | What Happens | Why |
|---|---|---|
| No service name set anywhere | The service is labelled “unknown service” followed by the program name | The default placeholder is used because nothing better was given |
Service name in OTEL_RESOURCE_ATTRIBUTES only | That value is used | The environment detector provides it |
Service name in both OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES | OTEL_SERVICE_NAME wins | The dedicated variable takes precedence over the general one |
| Two detectors report different host names | The one that runs later usually wins | Later results override earlier ones in a merge |
| Detector fails to find anything | Nothing is added, and earlier values stay | An empty result changes nothing |
The order of detectors matters
Because later detectors can override earlier ones, the order in which you list detectors is part of your configuration. If two detectors can disagree, such as one that reads a host name from the operating system and another that reads a more precise host ID from the cloud, put the more trusted source later.
Think of a form with several corrections. The first draft says the meeting is at 3 pm. A later note says it has moved to 4 pm, and the final note from the organiser says 4:30 pm. Everyone follows the last, most authoritative note. Merging resources works in the same spirit: the later and more explicit statement wins.
7The Environment Variable Detector
The simplest and most important detector. Almost everyone uses it.
An environment variable is a named setting that is handed to a program when it starts, a bit like a note stuck on the door before the program enters the room. OpenTelemetry defines standard names for the notes that describe your resource.
| Setting | What It Holds | Notes |
|---|---|---|
OTEL_SERVICE_NAME | The logical name of the service | The easiest way to avoid “unknown service”. Wins over the same value inside the general setting. |
OTEL_RESOURCE_ATTRIBUTES | A list of name and value pairs, separated by commas | Used for the version, environment, team, and anything else you want to add. Special characters need encoding. |
Why this approach is so popular
- No code changes. You set the values in your deployment, not in your program.
- Works in every language. The names are the same everywhere.
- Easy to automate. Deployment tools can fill in the version, the environment and the team for you, so every service is labelled the same way.
- Easy to change. You can fix a label by redeploying with a new setting, without rebuilding the application.
Production Example: A Standard Deployment Template
A platform team writes one deployment template used by every service in the company. The template fills in the service name from the project name, the version from the release tag, the environment from the target cluster, and the team from the repository owner. Every one of the two hundred services now reports the same set of labels, written the same way, with zero effort from application developers.
Values in OTEL_RESOURCE_ATTRIBUTES are split on commas, so a value that itself contains a comma, an equals sign or spaces must be encoded correctly, or the list may be read wrongly and attributes silently lost. Keep values simple, and check the exported data to confirm.
8Built-In Detectors Across Languages
Each language ships its own set of detectors, with similar ideas but different names and defaults.
Environment Detector
Reads the standard settings. Present in virtually every language and nearly always on.
Host Detector
Finds the host name and, in some languages, an ID and architecture.
Operating System Detector
Finds the operating system type and description.
Process Detector
Finds the process ID, executable name, the runtime, and in some cases the command used to start the program.
Container Detector
Reads the container ID, often from system files that the container runtime exposes, and is usually offered as an add-on package.
Cloud Detectors
Separate packages for Amazon, Google and Microsoft clouds, and others. They ask a metadata service about the instance, task or function.
How the languages differ in style
- Java: Detectors are packaged as modules, and the Java agent turns on a sensible set automatically. Cloud ones are often switched on with a setting. Some detectors can be listed or hidden using provider settings.
- Python: The core package has the environment and process detectors, and cloud and container detectors come as separate packages. A setting can list which detectors to use at startup.
- JavaScript and Node.js: The core resources package offers environment, host, operating system, process and service instance detectors, and cloud detectors are separate packages. Some detectors work asynchronously, since they may need to wait for a reply.
- Go: The resource package offers options you choose when building the resource, such as ones for the host, operating system, process, container, and from the environment, and cloud detectors live in a separate contrib area.
- .NET: Packages for host, operating system, process, container and cloud environments are available as add-ons, and the environment settings are read by default.
Because package names and defaults keep changing, a good habit is to run your application once in a test environment with a console exporter, print the resource, and read it. That one look tells you exactly what your detectors found and what is missing.
9Cloud Detectors and Metadata Services
How can a program learn which cloud region it is in? It asks a very friendly neighbour.
Cloud providers give every virtual machine, container task or serverless function a special local address, called a metadata service, that answers questions about itself. The address is only reachable from inside that machine. A cloud detector simply makes a small web request to this address and reads the reply, which includes facts such as the instance ID, region, zone and account.
sequenceDiagram
participant App as Application with SDK
participant CD as Cloud detector
participant MS as Cloud metadata service
App->>CD: Start detection at startup
CD->>MS: Request token if the provider requires one
MS-->>CD: Short lived token
CD->>MS: Ask for instance ID, region and zone
MS-->>CD: Answers
CD->>CD: Turn answers into cloud.* and host.* attributes
CD-->>App: Return attributes within the allowed time
The cloud families
| Where You Run | What a Detector Can Learn | Points to Know |
|---|---|---|
| Virtual machines on a major cloud | Instance ID, type, image, region, zone, account | The metadata service must be reachable and not blocked by network rules |
| Managed container platforms | Cluster, task or service name, launch type, region | Often reads a metadata address that the platform places in an environment variable |
| Managed Kubernetes services | Cluster name and cloud details, along with the node’s information | The cluster name is sometimes only available from the cloud provider |
| Serverless functions | Function name, version, region, memory size, request ID details | Facts are mostly found in environment variables, with no network call needed |
| Application platforms as a service | Site or app name, region, resource group | Facts come from platform-provided environment variables |
Costs and cautions of asking the network
- Startup delay. A web request takes time. If the metadata service is slow or does not answer, startup may be delayed until a timeout. Good detectors use short timeouts.
- Failure must be harmless. If the program is running on a laptop and there is no metadata service, the detector must quietly return nothing. It must never stop the application from starting.
- Security tokens. Some clouds require a token, to protect the metadata service from abuse. Detectors handle this, but your network and permission settings must allow it.
- Containers and hops. In some container setups, the metadata service is only reachable if network settings allow an extra hop. If detection mysteriously finds nothing in a container, check this.
- Short-lived functions. In serverless functions that start often, every extra millisecond of startup is paid again and again. Prefer detectors that read environment variables over those that make network calls.
Think of arriving at a big airport and not knowing which terminal you are in. You can look at a map on the wall, which is quick, or ask a staff member at the information desk, which takes longer and might have a queue. A cloud detector that reads an environment variable is like looking at the wall map. One that calls the metadata service is like going to the desk.
10Kubernetes: A Special Case
Inside a container, the program does not automatically know which pod or node it is in.
Kubernetes is a popular system for running many containers across many machines. Inside a container, a program sees its own small world. It does not directly know the name of its pod, the namespace, or the node. These facts are held by Kubernetes, not by the program, so a simple detector cannot discover them by looking around. We need a way to hand these facts in.
Three ways to get Kubernetes facts into the resource
The Downward API
Kubernetes can pass facts about a pod into its containers as environment variables, such as the pod name and namespace. You then copy these into the resource attributes setting.
The OpenTelemetry Operator
When it injects auto-instrumentation, the Operator can automatically set resource attributes from the pod’s own information, so you do nothing.
The Collector’s Kubernetes Enrichment
A Collector processor looks up the pod in the Kubernetes API, using the network address of the sender, and adds pod and namespace details to the data in transit.
flowchart TD
K["Kubernetes pod definition"] --> D["Downward API passes pod name and namespace as environment variables"]
K --> O["Operator injects settings when instrumenting"]
D --> E["OTEL_RESOURCE_ATTRIBUTES includes k8s facts"]
O --> E
E --> S["SDK environment detector builds the resource"]
S --> X["Telemetry carries k8s attributes"]
K --> A["Kubernetes API"]
A --> P["Collector Kubernetes attributes processor looks up the sender"]
X --> P
P --> Y["Telemetry enriched in the Collector"]
A very important difference: detecting versus looking up
This is a favourite source of confusion, so let us be careful. The two Collector tools sound similar but do different jobs.
| Question | Resource Detection Processor | Kubernetes Attributes Processor |
|---|---|---|
| What does it describe? | The environment where the Collector itself runs | The environment of each sender whose data passes through |
| How does it learn? | By looking around, for example at the host and cloud metadata | By asking the Kubernetes API about the sender’s pod |
| Good when | The Collector runs on the same machine, in the same pod or the same node as the apps, as an agent | The Collector receives data from many pods and must tell them apart |
| Wrong when | The Collector is a central gateway, since all data would be stamped with the gateway’s own details | Used without permission to read the Kubernetes API |
Imagine a post office clerk stamping every parcel. If the clerk uses the post office’s own address stamp, the stamp describes the post office, not the sender. That is the detection processor on a central gateway. If the clerk instead looks up each sender in a directory and writes the correct address on each parcel, that is the Kubernetes attributes processor.
A very common mistake is to add the resource detection processor to a central gateway Collector and then wonder why every service in the company appears to be running on the gateway’s host. Detect the sender’s environment in the SDK, in an agent Collector beside the sender, or with the Kubernetes lookup, not in a distant gateway.
11The Resource Detection Processor in the Collector
The same idea, working inside the Collector pipeline.
In the Collector, the resource detection processor runs detectors and adds the facts it finds to the resource of the data that passes through. It is configured, not coded. You list the detectors you want, in order, and set a few options. It can add the same details to traces, metrics and logs.
The kinds of detectors it offers
Environment and System
Reads standard settings, and the host’s name, operating system and similar details.
Docker
Finds container host details from the container runtime.
EC2, ECS, EKS and Others
Reads facts for virtual machines, container tasks, managed Kubernetes, serverless and related platforms.
Compute, GKE and Cloud Run
Finds project, region, zone and platform details on Google’s cloud.
Azure and AKS
Reads subscription, resource group, region and platform details.
Other Platforms
Detectors exist for several other platforms and tools, depending on the Collector distribution you use.
The important settings
| Setting | What It Does | Why It Matters |
|---|---|---|
| Detectors list | Names the detectors to run, in order | Order decides who wins when two detectors disagree |
| Timeout | How long to wait for a detector to finish | Stops a slow metadata service from delaying startup for long |
| Override | Whether detected values replace attributes already on the data | Controls whether the Collector can overwrite what the SDK already set. Many teams set it so that existing values are kept. |
| Per-detector attribute choices | Which attributes each detector should or should not add | Lets you leave out sensitive or high-churn facts |
flowchart LR
R["Receiver"] --> M["Memory Limiter"]
M --> RD["Resource Detection"]
RD --> F["Filter and Redaction"]
F --> S["Sampling"]
S --> B["Batch"]
B --> X["Exporter"]
Resource detection normally sits early in the pipeline, right after the memory limiter. The reason is that later processors, such as filters and samplers, may want to use the labels it adds, for example “drop data from the test environment”. Adding labels first means the rules that come after can see them.
Production Example: Labels for Free on Every Node
A company runs one Collector agent on every machine in its cluster. The agent uses the resource detection processor with the system and cloud detectors. Every span, metric and log leaving any machine now carries the host name, cloud region and zone, even from old applications that were never taught to report these facts. Engineers can filter dashboards by zone on day one.
12Where Should You Detect: SDK or Collector?
The same facts can often be found in either place. Let us choose wisely.
| Fact | Better in the SDK | Better in the Collector |
|---|---|---|
| Service name and version | Yes. Only the application and its deployment know them. | No. The Collector cannot guess them reliably. |
| Process and runtime details | Yes. The SDK sits inside the process. | No. The Collector sees a different process. |
| Host and cloud facts | Possible, with add-on detectors | Yes, if the Collector runs beside the app as an agent |
| Kubernetes pod and namespace | Via the Downward API or the Operator | Via the Kubernetes attributes processor, which works for any sender |
| Company-wide labels such as team or cost centre | Via standard environment settings from a template | Via the resource processor, which can add fixed labels to everything |
| Old applications that cannot be changed | Not possible | Yes, the main reason to detect in the Collector |
flowchart TD
Q["Which fact do you need?"] --> A{"Does only the application know it?"}
A -->|Yes| B["Set it in the SDK with environment settings"]
A -->|No| C{"Can you change or restart the application?"}
C -->|Yes| D["Detect in the SDK, or pass it in with the Downward API or Operator"]
C -->|No| E{"Does the Collector run beside the application?"}
E -->|Yes| F["Use the resource detection processor in the agent Collector"]
E -->|No| G["Use a lookup, such as the Kubernetes attributes processor, or add fixed labels in the Collector"]
A sensible split of duties
- The application and its deployment own the identity: service name, version, environment and team.
- The agent Collector owns the local environment: host, cloud and node facts.
- The gateway Collector owns company-wide policies: adding or fixing labels, and removing sensitive ones.
Giving each layer one clear job avoids two layers fighting over the same attribute.
13Data Flow and Lifecycle of a Resource
Follow one resource from the moment your program starts to the moment you read it on a dashboard.
Deployment Sets the Stage
Your deployment tool passes settings such as the service name, the version and the environment, plus pod facts, to the container.
The Program Starts and the SDK Initialises
The SDK builds its default resource and then runs the configured detectors.
The Final Resource Is Frozen
All the facts are merged, and the resource is attached to the tracer, meter and logger providers.
Telemetry Is Exported With It
Each batch of spans, metrics or logs sent to the Collector includes the resource once at the top of the batch.
The Collector May Add or Change
An agent Collector may add host and cloud facts, and the Kubernetes lookup may add pod facts. A gateway may enforce company labels or remove sensitive ones.
The Backend Stores and Groups
The storage system keeps the resource facts and uses them to group data: for example, by service name, region and version.
Engineers Filter and Compare
On dashboards, people filter by service, compare versions, split by region and drill into one pod, all because the resource facts are present.
How the resource looks in different backends
Different tools treat resource facts in different ways. Some show them as a panel of properties on each trace. Some use the service name as the main grouping key. Some store them in a separate table that you can join with other data. For example, when metrics are sent to a Prometheus-style system, the service name and instance become the standard “job” and “instance” labels, and the rest of the resource usually goes into a special information metric, which you can join in a query. Choose which facts to turn into direct labels with care, because each extra label can multiply the number of stored series.
A resource is attached once per batch, not once per span. That means adding many resource attributes costs very little on the wire. The real cost shows up later, when a backend turns some of them into labels or indexes them, so be selective at that stage, not at the detection stage.
14Resource Rules: Immutability and What Not to Put There
A resource is a luggage tag, not a diary. Keep it stable.
Because a resource is created once and does not change, it is the wrong place for anything that changes while the program runs. Putting changing facts there leads to confusing data or forces you to restart programs just to update labels.
Good Resource Attributes
- Service name, version and namespace
- Deployment environment
- Cloud provider, account, region and zone
- Host, container and pod identity
- Fixed company labels, such as team
Bad Resource Attributes
- The current user, request or session
- Current memory or CPU use
- Feature flag values that can change while the program runs
- Anything that has a different value for each request
- Secrets, tokens or passwords
The churn problem
Some resource attributes change every time a program restarts, such as the process ID, the pod name and the container ID. That is correct, since a new pod really is a new sender. But in a metrics system, every new combination of labels creates a brand new series. A cluster that restarts pods often can produce a flood of short-lived series if these attributes are turned into labels. Keep these in the resource, but be cautious about promoting them to metric labels.
Think of a football match programme. It lists the team names, the stadium and the date. It does not list the current score, since that changes every minute. The programme is the resource. The live scoreboard is the span attributes and metrics.
15Instance Identity: Telling Copies Apart
When twenty copies of a service run at once, each needs its own identity.
The service instance ID, written service.instance.id, is meant to be unique for each running copy of a service. It lets you answer questions such as “is only one of the twenty copies slow?” The guidance is that it should be unique within the same service name and namespace, and it should be stable for the life of that copy.
Where does it come from?
- You can set it through the standard environment settings, for example using the pod name.
- A detector may create it, often as a random unique value generated at startup, in languages that offer such a detector.
- A platform may provide it, such as a cloud instance ID or a container ID that you reuse.
Why this small field matters
In metrics systems, the instance ID is used to keep the numbers from different copies apart. If two copies share the same instance identity, their numbers can collide, overwrite each other or double count. If there is no instance identity at all, the backend cannot tell which copy produced a number.
Production Example: The Overlapping Counters
A team ran several copies of a worker service, all without an instance ID. Their metrics system treated the numbers from all copies as one stream, so a counter seemed to jump up and down as copies reported in turn. Adding a unique instance ID to each copy through the deployment template instantly produced clean, separate lines for each copy.
16Semantic Conventions, Schema URLs and Name Changes
Names are agreed standards. Standards evolve, and that has consequences.
The names of resource attributes come from the semantic conventions. These are carefully designed, but they are still maturing, and a few names have been renamed over time as the community improved them. For example, the attribute that records the deployment environment has been moving to a more specific name in newer versions. This is healthy for the project but can surprise teams whose dashboards depend on the old names.
Schema URLs
To help with this, a resource can carry a schema URL, a web address that names the version of the conventions used. A tool can use this to understand which version of the names a piece of data follows, and translate between versions. When two resources with different schema URLs are merged, the rules get more careful, and in some cases the merge is refused to avoid mixing incompatible names.
flowchart LR
A["Old SDK sends deployment.environment"] --> C["Collector or backend"]
B["New SDK sends the newer environment name"] --> C
C --> D{"Are both names handled?"}
D -->|Yes| E["Dashboards show all services together"]
D -->|No| F["Some services vanish from filters and dashboards"]
How to stay safe
- Read the release notes before upgrading SDKs or agents, looking for renamed attributes.
- Test dashboards after an upgrade in a staging environment.
- Normalise in the Collector by adding a rule that copies old names to new names or the other way around, during migration.
- Pin versions across services, so that the whole company moves together instead of living with mixed names for months.
17Advantages, Disadvantages and Trade-offs
Every approach has a price. Let us count both sides honestly.
Advantages
- Less manual work: facts are found automatically
- Consistency: every service reports the same kinds of facts in the same way
- Accuracy: facts come from the real environment, not from memory or copy and paste
- Cheap to carry: attached once per batch, not once per item
- Great for filtering: region, zone, version and pod become instant filters
- Works for old applications: the Collector can add facts without code changes
Disadvantages
- Startup cost: network-based detectors can add delay
- Environment dependent: a detector finds nothing if the platform hides the facts
- Different defaults: languages and agents enable different detectors
- Privacy risk: some facts reveal more than you want to share
- Name churn: standards evolve and names change
- Confusing overlap: the SDK and Collector can both add the same facts and conflict
Key trade-offs
| Decision | Option A | Option B |
|---|---|---|
| Where to detect | In the SDK, accurate for the process and fully under developer control | In the Collector, central and works for old applications |
| How many detectors | Many, for rich context but longer startup and more privacy risk | Few, for lean startup and less exposure but less context |
| Cloud detection method | Metadata service calls, richest facts but slower | Environment variables only, faster but fewer facts |
| Override setting in the Collector | Overwrite existing values, so detected facts are consistent | Keep existing values, so application choices are respected |
| Promote to metric labels | Promote many, for easy queries but high series counts | Promote few and join the rest when needed |
18Security and Privacy Considerations
A luggage tag that says too much can be a problem. Be deliberate about what you reveal.
Command Lines Can Hold Passwords
A process detector may record the full command that started the program. If a password or token was passed as part of that command, it could end up in every piece of telemetry. Turn off or scrub such attributes.
Do Not Put Secrets in Resource Settings
The resource settings are visible in telemetry, in configuration files and in process listings. Never place keys, tokens or passwords there.
Account and Host Identifiers
Cloud account IDs, host names and addresses can reveal how your infrastructure is laid out. Share them only with backends you trust.
Metadata Services Need Care
Metadata services can also give out credentials in some clouds. Detectors should read only the harmless facts, and your network rules should limit which programs can reach the service.
Least Privilege for Lookups
A Kubernetes lookup needs permission to read pod information. Grant only the narrow rights needed, not wide access to the whole cluster.
Remove Before Export
A Collector can delete or mask sensitive resource attributes before data leaves your network. Treat this as a second line of defence.
Resource attributes ride along with every batch, so a sensitive value in the resource is repeated in all of your data, forever. Review a sample of real exported resources from a test environment before turning on a new detector in production, and check it against your privacy rules, such as GDPR.
19Monitoring, Logging and Troubleshooting
When labels go missing or look wrong, here is how to find out why.
Signals that something is wrong
- “Unknown service” in your tools: nobody set the service name for that program.
- Missing cloud or Kubernetes fields: a detector is not enabled, cannot reach its metadata source, or the platform hides the facts.
- All services showing the same host: detection is running in a central gateway instead of beside the sender.
- Services split in two on dashboards: two spellings or two naming versions are in use for the same service.
- Slow application start: a detector is waiting on a metadata service that never answers.
- Metric series exploding: restart-prone attributes such as pod name or process ID are being turned into labels.
- Values that look wrong or old: an attribute set by hand, or an old setting, is overriding the detected value.
A simple troubleshooting routine
Print the Resource
Run the application in a test environment with a console exporter, or look at the data arriving in the Collector, and read the exact resource.
Compare With What You Expected
List the attributes you expected, and tick off which are present, missing or surprising.
Check Which Detectors Are Enabled
Look at the SDK or agent settings, and at the Collector’s detector list and order.
Check the Environment
Confirm that the settings are actually present inside the running container, and that the metadata service is reachable from there.
Look at Precedence
See whether another source is overriding the value, whether in the SDK, or because the Collector’s override option is replacing it.
Read the Logs
Turn on debug logging briefly. Detectors often log what they tried and why they gave up.
Common symptoms and likely causes
| Symptom | Likely Cause | What to Try |
|---|---|---|
| Service appears as “unknown service” | No service name configured | Set the service name setting in the deployment |
| No cloud facts at all | Cloud detector not enabled, or metadata service unreachable | Enable the detector, check network rules and token requirements |
| All data shows the Collector’s host | Resource detection on a gateway Collector | Detect in the SDK or an agent Collector, or use a sender lookup |
| Pod name missing | Downward API not wired up, or Operator not used | Pass pod facts as environment variables, or enable the Kubernetes lookup |
| Attributes silently missing from the settings list | Encoding problem with special characters | Simplify values and encode special characters correctly |
| Version label differs between SDK and Collector | Both layers set it, and override is replacing one | Decide which layer owns it, and configure override accordingly |
| Application starts slowly in the cloud | A detector is waiting for a slow or blocked metadata call | Shorten the timeout, remove the detector, or use environment-based detection |
20Performance and Startup Considerations
Detection happens once, but when it happens matters.
Since detection normally runs once at startup, its cost is usually small and does not affect the speed of your requests. But there are cases where it matters, and they are worth understanding.
Serverless Functions
A function that starts many times per hour pays the detection cost on each start. Use detectors that read environment variables, and avoid ones that make network calls, if you can.
Metadata Service Delays
When a metadata service does not answer, the detector waits until its timeout. Keep timeouts short so a missing service does not hold up your start.
Synchronous versus Asynchronous
Some SDKs detect in the background, so the program starts quickly and the facts are added when ready. Others wait. Know which you have, and whether early telemetry might leave without the full resource.
Many Instances at Once
When hundreds of copies start together, they all query the same metadata service at once. This can cause rate limits, so avoid repeated retries.
For maximum predictability, a very common approach is to pass the important facts to your application through environment settings in the deployment, and keep network-based detection in the Collector agent. That way, the application starts instantly and still carries rich context.
21Design Patterns and Anti-Patterns
Habits worth copying and traps worth avoiding.
Good design patterns
Identity by Template
Generate service name, version, environment and team from your deployment template, so every service is labelled consistently.
Layered Ownership
The app owns identity, the agent Collector owns local environment facts, and the gateway owns company policy.
Downward API Wiring
Pass pod name, namespace and node name into containers and copy them into resource settings.
Detect Early in the Pipeline
Put resource detection right after the memory limiter, so later rules can use the labels.
Allow List of Attributes
Choose exactly which detected attributes you want to keep, and drop the rest.
Golden Resource Test
Add a simple automated check that every service’s exported resource contains the required attributes.
Anti-patterns
The Mistake
Leaving the service name unset and trusting the default.
Why It Hurts
Data from many services is grouped under placeholder names, and dashboards, alerts and service maps become meaningless.
Better Approach
Set the service name explicitly for every service, and add an automatic check that rejects “unknown service” in your pipeline.
The Mistake
Running the resource detection processor on a central gateway and expecting it to describe the senders.
Why It Hurts
Every service appears to be on the gateway’s host and region, hiding the real picture.
Better Approach
Detect in the SDK or in an agent Collector beside the sender, and use a lookup tool for Kubernetes facts.
The Mistake
Putting per-request or fast-changing data in resource attributes.
Why It Hurts
The resource is fixed at startup, so values go stale or force restarts, and metrics systems may be flooded with series.
Better Approach
Use span attributes or metric attributes for changing values, and keep the resource for stable identity.
The Mistake
Enabling every available detector everywhere “just in case”.
Why It Hurts
Startup gets slower, privacy risks increase, and noisy or churning attributes pollute your data.
Better Approach
Enable only the detectors that answer questions you actually ask, and review what they add.
The Mistake
Letting the SDK and the Collector both set the same attribute with different values and no clear owner.
Why It Hurts
Values flip depending on the override setting, producing inconsistent data that is hard to explain.
Better Approach
Assign each attribute to one layer, and configure the override option to match.
The Mistake
Hard-coding host names or instance IDs into configuration files that are shared among many copies.
Why It Hurts
Many copies report the same identity, so metrics collide and traces cannot be tied to a single copy.
Better Approach
Derive identity at start time from the platform, such as the pod name, or let a detector generate a unique instance ID.
22Best Practices and Common Mistakes
A practical checklist for resources you can trust.
Best practices
- Always set the service name. It is the first thing every tool uses, and the most common reason for confusing dashboards.
- Set version and environment. They let you compare releases and separate production from testing.
- Provide a unique instance ID. It keeps copies of the same service apart in traces and metrics.
- Use the standard names. Follow the semantic conventions, and avoid inventing your own spellings.
- Standardise with templates. Let your deployment tools fill in labels, so developers cannot get them wrong.
- Decide where each fact is detected. One owner per attribute, with the Collector override setting matching that choice.
- Use short timeouts. Detectors that call the network must never be allowed to hold up your startup for long.
- Review before production. Print real resources in a test environment, and check them for privacy and correctness.
- Be selective with labels in metrics. Keep most resource facts in the resource, and promote only a short list.
- Watch naming changes. Read release notes, test dashboards and normalise old and new names during upgrades.
- Test it. Add an automatic test that checks the presence of required resource attributes for every service.
Common mistakes beginners make
- Believing that detectors set the service name for you. They usually cannot know it.
- Mixing up the resource detection processor with the Kubernetes attributes processor.
- Forgetting that a resource cannot change after the SDK starts.
- Assuming that every detector is on by default in every language.
- Using the resource for data that changes per request.
- Not checking what was actually exported, and trusting the configuration instead.
- Letting secrets slip into resource settings or captured command lines.
- Promoting too many resource attributes to metric labels.
- Ignoring conflicts between the SDK and the Collector.
23Real-World and Industry Examples
How resource detectors show up in tools and platforms you may already know.
Amazon Web Services
The AWS Distro for OpenTelemetry and the open-source detector packages for Amazon’s cloud can recognise virtual machines, container tasks, managed Kubernetes clusters and serverless functions, and add facts such as region, account and task identity to telemetry. This gives teams on Amazon’s platforms rich labels without writing any code of their own.
Google Cloud
Google publishes resource detector libraries for its cloud, which discover facts such as project, region and zone for virtual machines, managed Kubernetes and serverless platforms. Google’s monitoring services use these labels to group and map data, which shows how a good detector makes a backend more useful.
Microsoft Azure
Azure’s monitoring products read the service name and namespace from the resource to decide how a service is shown in application maps, and detectors for Azure’s compute and Kubernetes services add subscription, resource group and region facts. A missing or wrong service name can cause several services to be shown as one.
The OpenTelemetry Operator on Kubernetes
When the Operator injects auto-instrumentation into a workload, it can automatically supply resource attributes taken from the pod, such as namespace and pod name. A platform team gets consistent Kubernetes labels on every service in a cluster by adding one annotation, with no application changes.
Agent Collectors Across Fleets
Large organisations run one Collector agent on every machine. By turning on the resource detection processor there, they add host and cloud details to the telemetry of every program on the machine, including programs from vendors that cannot be modified. This is often the first place teams see the benefit of centralised detection.
A typical journey for a growing company
Name Your Services
Set the service name, version and environment in every deployment, fixing the “unknown service” problem first.
Add Local Facts
Enable host, process and container detectors in the SDK or agent, and review the output.
Add Cloud and Cluster Facts
Use cloud detectors, the Downward API, the Operator or the Kubernetes lookup to add region, zone, pod and namespace.
Centralise Policy
Use a gateway Collector to enforce company labels, remove sensitive attributes and normalise old and new names.
Govern and Test
Add automatic checks for required attributes, review privacy regularly, and plan carefully for name changes.
24Frequently Asked Questions
Quick answers to questions beginners ask most.
25Summary and Key Takeaways
If you remember only a few things, remember these.
Key Takeaways
- A resource is the luggage tag of your telemetry. It describes the sender, such as the service, host, cloud and container, and is attached once to every trace, metric and log.
- Resource detectors fill in the tag automatically. They look at the environment, such as the machine, process, container, cloud and Kubernetes, and turn what they find into standard attributes.
- You still must name your service. Detectors cannot guess the logical service name, version or environment, so set them with standard settings, or you will see “unknown service”.
- Order and precedence matter. When sources disagree, later and more explicit values win, and the dedicated service name setting wins over the general one.
- Detect in the right place. The SDK owns identity, an agent Collector beside the app owns host and cloud facts, and a central gateway should not pretend to describe distant senders. Use the Kubernetes lookup for pod facts.
- Keep the resource stable and the labels few. Do not put changing or secret values there, and promote only a short list of resource attributes to metric labels.
- Check, protect and plan. Print real resources before going live, scrub sensitive attributes, use short timeouts for network detectors, and prepare for naming changes between versions.