What Is a Resource in OpenTelemetry?

What Is a Resource in OpenTelemetry?

A plain-English guide to the “who is speaking” part of your telemetry — the set of labels that tells every trace, metric, and log which service, machine, and environment it came from.

Imagine you receive a thousand postcards in your mailbox. Each one describes a lovely day: sunshine, a swim in the sea, a delicious dinner. Now imagine that none of the postcards has a name or a return address. You can read what happened, but you cannot say who wrote it, where they were, or when they traveled. The postcards are charming and almost completely useless.

Software telemetry can suffer from exactly this problem. A program may record that a request took two seconds, or that a database call failed. But if the record does not say which program, which version, which machine, and which environment produced it, the engineer reading it is stuck. They know that something was slow, but not where to look. OpenTelemetry solves this with a small, powerful idea called a Resource.

This guide explains resources from the ground up. You will learn what a resource contains, where its values come from, how it travels with your data, how it differs from other labels, and how to choose its values wisely and safely. Every new word is explained the first time it appears, so you do not need any earlier knowledge of observability.

1What Is a Resource?

The short answer first, then the details that make it useful.

A Resource in OpenTelemetry is a description of the thing that produces telemetry. That “thing” is usually a running program, together with the machine, container, or cloud platform that it runs on. The description is made of simple labels called resource attributes, each one a name paired with a value, such as a service name, a version number, a region, or a pod name.

Let us unpack some words. OpenTelemetry, often shortened to OTel, is a free, open source framework hosted by the Cloud Native Computing Foundation. It gives developers one standard way to create and send telemetry, which is the stream of traces, metrics, and logs that describes how software behaves. A trace follows one request through many services. A metric is a number measured over time. A log is a written record of an event.

Every one of those signals needs context. A resource supplies it. The resource is attached once, at the level of the whole program, and every span, metric, and log record produced by that program carries it along. You do not repeat it on each individual item. It is like a name badge pinned to a person, which everyone can read for as long as the person is in the room.

3
Signals that share one resource: traces, metrics, and logs
1
Resource per provider, fixed once the program has started
1
Name that matters most: service.name
Everyday Analogy

Think of a hospital patient wearing a wristband. The wristband carries the patient’s name, birth date, and ward. Every nurse, doctor, and test result refers to that wristband, so nothing gets mixed up between patients. A resource is the wristband of a program. A span, a metric, or a log message is a test result. Without the wristband, a result is only a number. With it, the number belongs to a particular person.

What a resource usually answers

  • Which service? The name of the program or application, such as checkout or payments.
  • Which version? The release number, so you can tell whether a problem began with a new deployment.
  • Which environment? Production, staging, or testing, so test noise never mixes with real customers.
  • Which machine? The host, container, or pod that ran the code.
  • Which place? The cloud provider, region, and zone where it all lives.
  • Which tools? The OpenTelemetry library, its language, and its version.

What a resource is not

A resource is not a span attribute. A span attribute describes one particular operation, such as one web request. A resource describes the whole program that made it. A resource is also not baggage, which is a separate bundle of information that travels from service to service along with a request. And a resource is not a place to store changing values. It is set when the program starts and stays the same for the program’s whole life.

i
Remember This

A span says what happened. A resource says who it happened to. If you can answer “which service, which version, and which machine?” for every piece of telemetry, your resources are doing their job.

A Closer Look at the Idea of Identity

Every complicated system eventually faces the same question: who is talking? A hospital must know which doctor wrote a note, a bank must know which branch approved a payment, and a delivery company must know which truck carried a parcel. Without identity, records are only piles of facts. Telemetry is no different. A trace, a metric, or a log message is a fact about something that happened, but the fact becomes useful only when you know which program, which copy, and which environment produced it.

The resource solves identity in a simple and economical way. Instead of repeating the sender’s details inside every tiny message, the program describes itself once, at startup, and the description travels with every batch of data. Readers of the data, whether people or tools, can then group, filter, compare, and route telemetry by producer. This design choice keeps messages small, keeps names consistent, and lets different tools agree on what a label means.

Notice also what the resource deliberately does not do. It does not describe a single request, a single customer, or a single moment. Those details change constantly and belong elsewhere. The resource describes things that stay true while the program runs: its name, its version, its machine, its cloud, its region. This separation between “who is speaking” and “what was said” is the heart of the whole concept, and every later chapter builds on it.

2Why Resources Exist: A Short History

Every good idea begins as a response to a painful problem. Resources are no exception.

In the early days of monitoring, programs usually ran on a handful of well-known machines, and each machine had a name that everybody remembered. When a metric looked strange, an engineer would say, “it is the server in rack three,” and that was enough. The name of the machine was the only context anyone needed.

Then software changed shape. Companies split big programs into dozens of small services. Services began running in containers that start and stop many times a day. Containers were scheduled across fleets of machines, and fleets moved into the cloud. Suddenly a single service might be running as fifty identical copies, which appear and disappear within hours. The old habit of remembering machine names stopped working. Context had to travel with the data, and it had to describe things such as services, versions, and pods instead of just servers.

Early monitoring products solved the problem each in its own way. One product asked you to add “tags,” another called them “labels,” and a third called them “dimensions.” Each had its own spellings and rules, so the same idea appeared under many different names. If you moved from one product to another, you had to rename everything. Open source projects such as OpenCensus already carried a notion of a resource, and when OpenTracing and OpenCensus merged into OpenTelemetry in 2019, the idea became a first-class part of the new standard.

1

Named machines

A few well-known servers produce data. The machine name is all the context anyone needs.

2

Microservices and containers

Many small, short-lived copies of services replace a few long-lived servers. Machine names stop being useful.

3

Product-specific tags

Each monitoring product invents its own tags, labels, or dimensions, with different names and different rules.

4

2019 — OpenTelemetry

The merged project makes a resource a standard concept, shared by traces, metrics, and logs.

5

Semantic conventions

The project publishes agreed names for common attributes, so a service name is always called the same thing everywhere.

6

Richer models

Newer work, called entities, aims to describe the different parts of the producer, such as the service, the host, and the pod, in a more structured way.

Why Agreed Names Matter

Imagine a school where every teacher writes the student’s name in a different place on the exam paper. One uses the top left, another the bottom right, a third the back page. Marking becomes a scavenger hunt. A school rule that says “write your name in the top right corner” saves everyone time. Semantic conventions are that rule for telemetry: everyone puts the service name in the same place, with the same spelling.

i
A Word You Will Meet Often

Semantic conventions are the project’s agreed names and meanings for common attributes. They are published as a shared reference, and they are the reason that a dashboard written for one team’s data can often work for another team’s data without changes.

3Core Concepts Around Resources

A handful of words explain nearly everything in this area. Let us meet them one at a time.

Concept

Resource

The complete description of the producer of telemetry, made of many attributes. Once the program starts, it does not change.

Concept

Resource attribute

One label inside a resource: a name and a value. Values can be text, numbers, true or false answers, or short lists.

Concept

Semantic convention

The agreed spelling and meaning for a common attribute, such as service.name. It keeps everyone speaking the same language.

Concept

Resource detector

A small piece of code in the SDK that discovers attributes automatically, such as the host name, the operating system, or the cloud region.

Concept

Provider

The object inside the SDK that creates telemetry for one signal. There is a tracer provider, a meter provider, and a logger provider, and each holds a resource.

Concept

Instrumentation scope

The name and version of the library that recorded a piece of telemetry. It answers “which code made this?” while the resource answers “which program was running?”

Concept

Schema URL

An optional web address that names the version of the semantic conventions in use, so tools can handle renamed attributes correctly over time.

Concept

Entity

A newer way of thinking about a resource as several connected things, such as a service, a host, and a container, each with its own identity.

Attributes: the building blocks

An attribute is a pair of a key and a value. The key is the name, for example service.name, and the value is the answer, for example checkout. The dots in the key are just a naming habit, used to group related keys, so that everything about the service starts with service, everything about the host starts with host, and so on. Resource attributes are the same kind of pairs as span attributes. The difference lies in where they are attached and how often they change.

The SDK and the Collector

Two programs often take part. The SDK is the OpenTelemetry code that lives inside your application. It builds the resource when your program starts. The Collector is a separate program that receives telemetry, changes it if needed, and forwards it onward. The Collector can also add or adjust resource attributes, especially ones that the application cannot easily know about itself, such as which Kubernetes node it runs on. We will return to this combination later.

i
A Simple Mental Model

Picture a factory that ships parcels. The resource is the return address printed on every box. The instrumentation scope is the name of the packing station that closed the box. The attributes on a span are the notes written on one specific box. The return address is the same for the whole day, while the notes change from box to box.

4The Anatomy of a Resource

Open a resource like a filing cabinet and look at every drawer.

The semantic conventions organize resource attributes into groups. Not every program uses every group. A small program on a laptop may have only a service name and a few details about the process. A large program in a Kubernetes cluster on a public cloud may have attributes from almost every group. The table below describes the most common groups.

GroupTypical attributesWhat it tells you
Serviceservice.name, service.version, service.namespace, service.instance.idWhich program, which release, which team group, and which copy
Telemetry SDKtelemetry.sdk.name, telemetry.sdk.language, telemetry.sdk.versionWhich OpenTelemetry library, in which language, and which version produced the data
Deploymentdeployment.environmentWhether this is production, staging, or testing
Hosthost.name, host.id, host.archWhich machine and what kind of processor
Operating systemos.type, os.descriptionWhich operating system
Processprocess.pid, process.runtime.name, process.runtime.versionWhich running process and which language runtime
Containercontainer.id, container.name, container.image.nameWhich container and which image it was built from
Cloudcloud.provider, cloud.platform, cloud.region, cloud.availability_zone, cloud.account.idWhich cloud, which product, and where it is
Kubernetesk8s.cluster.name, k8s.namespace.name, k8s.pod.name, k8s.deployment.name, k8s.node.nameWhich cluster, which group of workloads, and which pod
Serverlessfaas.name, faas.version, faas.instanceWhich function, which version, and which running copy

The star attribute: service.name

Of all the attributes, one deserves special respect: service.name. It is the logical name of your program, such as “checkout” or “inventory.” Almost every observability tool groups, filters, and draws diagrams by service name. If you leave it unset, the SDK fills in a placeholder that begins with unknown_service, often followed by the name of the program file. Every program that shows this placeholder is a sign that nobody finished the setup.

Service attributes in more detail

service.version records the release of your program, for example a version number or a short label from your build system. It lets you compare behavior before and after a deployment. service.namespace groups related services together, such as all services owned by the shopping team. And service.instance.id identifies one particular running copy, so that you can tell apart several identical copies of the same service. It is often a random unique string, because what matters is only that no two live copies share the same one.

Telemetry SDK attributes

The SDK adds its own attributes automatically: the name of the SDK, the programming language, and the version. They may look boring, but they are very helpful during an investigation. If strange data comes from one service only, checking that it uses an old or unusual SDK version often explains the surprise.

!
Not All Names Are Final

The semantic conventions keep evolving, and some attribute names have been renamed over time. For example, the attribute that records the deployment environment has been adjusted in newer versions of the conventions. When you see two spellings for the same idea, check which version of the conventions your tools expect, and use the schema URL feature or a Collector rule to bridge the difference.

5Resource, Scope, Span Attributes, and Baggage

There are several kinds of labels in OpenTelemetry. Knowing which one to use is half the skill.

Beginners often ask, “Why can I not just put everything in span attributes?” The answer is that different labels have different jobs, different lifetimes, and different costs. Putting a label in the right place makes data smaller, searches faster, and dashboards clearer.

Kind of labelAttached toHow often it changesExample
Resource attributeEverything a program producesNever, after start-upservice.name, k8s.pod.name
Instrumentation scopeEverything from one libraryNever, for a given libraryThe name of a web framework library
Span, metric, or log attributeOne operation, one data point, or one recordEvery timehttp.route, a status code
BaggageA request as it travels between servicesPer request, set deliberatelyA customer tier
Everyday Analogy

Think of a traveler at an airport. The passport says who the traveler is and stays the same all trip; that is the resource. The boarding pass is for one flight and changes with each leg; that is a span attribute. The stamp from the airline desk says which counter checked them in; that is the scope. And the note a friend slips into the bag that every airport official can read as the traveler moves along is baggage.

How the pieces nest inside the data

OpenTelemetry sends data in a standard format called OTLP, the OpenTelemetry Protocol. In OTLP, the pieces are arranged like nested boxes. The outer box is the resource. Inside it are boxes for each instrumentation scope. Inside each scope box are the actual spans, metric data points, or log records. Because the resource sits in the outer box, it is written only once for all of the items inside it, not once per item.

flowchart TD
    BATCH["One OTLP export request"]
    R1["Resource: checkout service, version 2.4, pod checkout-7f9c"]
    S1["Instrumentation scope: web framework library"]
    S2["Instrumentation scope: database driver library"]
    SP1["Span: GET checkout"]
    SP2["Span: validate cart"]
    SP3["Span: SELECT orders"]
    BATCH --> R1
    R1 --> S1
    R1 --> S2
    S1 --> SP1
    S1 --> SP2
    S2 --> SP3
Figure 1: The nesting of an OTLP request, with one resource shared by every scope and span inside it

Read the diagram from the top. An export request carries one resource, which names the checkout service, its version, and its pod. Inside the resource are two scope groups: one for the web framework library and one for the database library. Each scope holds its own spans. All three spans share the same resource without repeating it. This design is both tidy and efficient, because a batch of thousands of spans carries its resource only once.

Choosing the right home for a label

Ask three questions. First: does this value stay the same for the whole life of the program? If yes, it probably belongs in the resource. Second: does it describe the program or the machine, not a single operation? Again, that points to the resource. Third: does it change from one operation to the next, such as a user ID or a web address? Then it belongs to the span, metric, or log record. When in doubt, remember the rule: resource for who, attributes for what.

i
A Quick Test

Imagine restarting your program. If a label would have a new value after the restart, such as a pod name or a process number, it is a resource label of the new instance. If a label would be different on every single request, it does not belong in the resource at all.

Choosing the Right Home for a Fact

When you hold a new piece of information and wonder where to place it, ask three simple questions. First, does it stay the same for as long as the program runs? If yes, it is a candidate for the resource. Second, does it describe a single operation, such as one request or one database call? If yes, it belongs on a span or a log record. Third, does it need to travel across service boundaries so that other services can see it? If yes, it is a candidate for baggage, and you should be careful about what you put there, because baggage is copied into outgoing network calls and can leak.

Consider a few examples. The name of the cloud region stays fixed, so it goes into the resource. The route of one web request changes with each request, so it goes onto the span. A customer tier that downstream services need in order to behave differently could travel as baggage, though many teams prefer to add it explicitly where needed. A scope describes the library that created the telemetry, such as an HTTP instrumentation package, and tells you which piece of code produced a signal, while the resource tells you which program contained that code.

Mixing these levels is the most common source of confusion for beginners. A helpful mental picture is a set of nested boxes: the largest box is the resource, inside it sit the scopes, and inside each scope sit the individual spans, metrics, and logs. Anything you put in an outer box is shared by everything inside it.

6Where Resource Attributes Come From

Nobody types out fifty labels by hand. Several sources work together to build the resource for you.

When your program starts, the OpenTelemetry SDK builds its resource by gathering information from several places and combining them. This process is called merging. Understanding the sources helps you answer the common question, “Where did that value come from, and how do I change it?”

flowchart LR
    A["SDK defaults: telemetry SDK details and a placeholder service name"] --> M
    B["Resource detectors: host, operating system, process, container, cloud"] --> M
    C["Environment variables: OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME"] --> M
    D["Values set in application code"] --> M
    M{"Merge: later source wins on conflict"} --> R["Final resource that never changes"]
    R --> T["Tracer provider"]
    R --> U["Meter provider"]
    R --> V["Logger provider"]
Figure 2: Four sources are merged into one resource, which is then shared by all three providers

Read the diagram from the left. First come the SDK defaults: details about the SDK itself and a placeholder service name. Next come resource detectors, which look around and discover facts about the host, the operating system, the process, the container, and the cloud. Then come environment variables, which let whoever deploys the program set attributes from outside, without changing code. Last come values set directly in code. At the merge step, when two sources provide the same attribute, the later one wins. The result is the final resource, which is then given to the tracer provider, the meter provider, and the logger provider.

Resource detectors

A resource detector is a small helper that asks the environment questions and turns the answers into attributes. A host detector asks for the machine name. An operating system detector asks which system is running. A process detector asks for the process number and the language runtime. A container detector looks for a container identity. Cloud detectors know how to ask a specific platform, such as a virtual machine service or a container service, where it is running. Which detectors are on by default depends on the programming language and the way you set up the SDK, so check your language’s documentation, and turn on the ones you need.

Environment variables

Two environment variables are especially useful. OTEL_SERVICE_NAME sets the service name directly. OTEL_RESOURCE_ATTRIBUTES sets any number of other attributes, written as a comma-separated list of name and value pairs. If both set the service name, the dedicated service-name variable takes priority. Environment variables are popular because the people who deploy software, such as a platform team, can set them in a deployment file without asking developers to change and rebuild code.

SourceBest forWatch out for
SDK defaultsTelemetry SDK details, filled in automaticallyThe placeholder service name, which means setup is unfinished
DetectorsFacts the machine already knows, such as host and cloudDetectors can add noisy or sensitive values, and may be off by default
Environment variablesService name, version, environment, and team labels set at deployment timeTyping mistakes in the list, and keys that differ between teams
CodeValues the program itself must decideHard-coded values that drift out of date
Everyday Analogy

Think of filling in a registration form for a new employee. Some boxes are pre-printed by the company, such as the company name. Some are filled by the building’s scanner, such as the photo and the entry time. Some are written by the manager, such as the department. And a few are added by the employee. If the manager and the employee write different things in the same box, the form says the last entry counts. The resource merge works the same way.

!
Conflicting Schema Versions

When two resources are merged and each names a different version of the semantic conventions, the SDK may not be able to combine them safely, and it may drop the schema information. This is rare in simple setups, but it is worth knowing when you combine custom code with vendor distributions.

7The Lifecycle of a Resource

A resource is born when your program starts, travels with every piece of telemetry, and is finally stored in a backend.

flowchart LR
    A["1. Program starts"] --> B["2. SDK builds the resource"]
    B --> C["3. Providers hold the resource"]
    C --> D["4. Spans, metrics, and logs are created"]
    D --> E["5. Exporter groups items by resource"]
    E --> F["6. OTLP request sent"]
    F --> G["7. Collector may enrich the resource"]
    G --> H["8. Backend stores and indexes it"]
Figure 3: The eight stages of a resource, from program start to backend storage

Stage by stage

Starting. When the program begins, the SDK runs its set-up code. At this moment it collects the defaults, runs the detectors, reads the environment variables, and applies anything set in code. This is the only moment when the resource is built.

Holding. The resulting resource is given to each provider. A provider is the object that later creates telemetry for one signal. From now on, the resource is immutable, a word that simply means it cannot be changed. If you need different values, you start a new program or create a new provider with a new resource.

Creating telemetry. As your code does its work, spans, metrics, and log records are created. None of them carries a copy of the resource inside it. They only point to the provider that made them, and through the provider, to the resource.

Exporting. When the exporter packs items for sending, it gathers them into groups by resource and scope. The OTLP request then contains each resource once, followed by everything that belongs to it.

Enriching and storing. A Collector on the way may add or adjust attributes. Finally, the backend stores the telemetry and indexes the resource attributes so that you can search and group by them.

Why resources are cheap to send

Imagine a batch of 2,000 spans. A typical resource might take up about 400 bytes when written out. If every span carried its own copy, the batch would include 2,000 times 400 bytes, which is 800,000 bytes, close to a whole megabyte, just for repeating the same labels. Because the resource sits in the outer box and is written once, the batch carries only 400 bytes of resource, plus the actual span data. The saving grows with every item in the batch, and it is a quiet reason why putting program-level labels in the resource, instead of copying them into every span, is the efficient choice.

i
Restart Means a New Identity

Because a resource cannot change, a restart gives you a new set of values for anything unique to the copy, such as the process number or the pod name. That is exactly what you want: after a restart, the new copy should be distinguishable from the old one. Just be careful about which of those unique values later become labels in your metrics system, a point we revisit in the chapter on metrics backends.

i
What About Values That Truly Change?

Sometimes a value looks like part of the resource but actually changes while the program runs, such as the number of items currently in a queue, or the name of the customer being served. Those are measurements or span attributes. If you find yourself wanting to update a resource value at runtime, it is a sign that the value belongs somewhere else.

What Happens If Something Goes Wrong at Startup

Because the resource is built once, at the very beginning, mistakes made at that moment last for the whole life of the program. If a detector fails because the cloud information service is slow or unreachable, the program usually keeps running without those attributes. Telemetry flows, but it carries less context than you hoped. If an environment variable contains a typing error, the typo is copied faithfully into every batch until the next restart.

This is why the startup moment deserves attention. Teams that care about quality print the resource at startup in a test environment, compare it to the expected list, and fail the deployment if something essential is missing. They also keep a short timeout on detectors, so a slow cloud service delays startup by a fraction of a second rather than by half a minute.

It is equally important to remember what happens at shutdown. When a program stops cleanly, it should flush any waiting telemetry so that the final batch, which carries the same resource, reaches its destination. Programs that are killed abruptly can lose the last few seconds of data, and this is usually the data you most want to see when investigating a crash.

Finally, remember that restarts create new identities. A new copy of a service receives a new instance identifier, and in container platforms usually a new pod name as well. Dashboards that group by instance will therefore show old copies disappearing and new ones appearing. This is normal, and it is the reason that stable labels such as the service name, not instance labels, are the right basis for long-term charts and alerts.

8Resources in the Collector

Applications know their own name, but they often do not know where they live. The Collector can fill in the missing pieces.

An application running inside a container usually has no easy way to learn which cluster, which node, or which deployment it belongs to. The platform knows these facts, and the Collector can ask the platform. That is why many organizations add resource attributes in the Collector instead of, or in addition to, the application. Three Collector components do most of this work.

Processor

Resource processor

Adds, changes, or removes resource attributes by simple rules. It is the plain editing tool, handy for setting an environment label or deleting an unwanted attribute.

Processor

Resource detection processor

Looks at the machine the Collector runs on and adds facts such as the host name, cloud provider, region, and zone. It uses the same detective idea as the SDK, but inside the Collector.

Processor

Kubernetes attributes processor

Asks the Kubernetes system about the sender and adds the pod name, namespace, deployment, and node. It is the most popular way to add Kubernetes context.

Processor

Transform processor

A more powerful editor that can rewrite, copy, and compute values, including resource attributes, using a small rule language.

sequenceDiagram
    participant A as Application SDK
    participant G as Agent Collector on the node
    participant K as Kubernetes API
    participant B as Backend
    A->>G: 1. Send spans with a basic resource
    G->>G: 2. Detect host and cloud details
    G->>K: 3. Look up the pod by sender address
    K-->>G: 4. Return pod, namespace, deployment, node
    G->>G: 5. Add the Kubernetes attributes to the resource
    G->>B: 6. Export enriched telemetry
    Note over B: 7. Search and group by service, namespace, and pod
Figure 4: An agent Collector enriching a basic resource with Kubernetes details before export

Follow the story. In step one the application sends spans carrying only a basic resource, with the service name and little else. In step two the agent Collector, which runs on the same node, detects host and cloud details. In steps three and four it asks the Kubernetes system, “Which pod sent this?” and receives the pod name, the namespace, the deployment, and the node. In step five it adds those facts to the resource, and in step six it exports the enriched data. By step seven, the backend can group data by service, namespace, and pod, even though the application never knew those details.

How the Kubernetes processor knows who sent the data

The processor can match telemetry to a pod in several ways. The simplest is by the network address that the data came from, because each pod has its own address. It can also match by attributes that the application sends, such as the pod identity placed in the resource by the operator or by environment variables. For the lookup to work, the processor needs permission to read pod information from the Kubernetes system, which is granted through standard access rules. If those permissions are missing, the processor quietly adds nothing, which is a common cause of mysteriously empty Kubernetes labels.

Agent first, gateway second

Enrichment works best in an agent Collector that runs on the same node as the application, because the agent sees the true address of each sender. A shared gateway Collector farther away may only see the address of a proxy or a previous Collector, which makes address-based matching unreliable. A common design is therefore to enrich in the agent and apply company-wide rules, such as masking and sampling, in the gateway.

!
Who Wins When Both Set a Value?

If the application already set an attribute and a Collector processor also sets it, one of them will overwrite the other, depending on the processor’s settings. Decide in advance who owns each attribute. A clear rule such as “applications own service labels and the platform owns Kubernetes and cloud labels” prevents confusing disagreements.

Why Many Teams Enrich in the Collector

Adding Kubernetes details inside every application would mean giving every application permission to ask the cluster about itself, and teaching every programming language and library to do so. That is a great deal of repeated work and a larger attack surface. A Collector, in contrast, runs as one managed component. It can be given a narrow set of read permissions once, it can look up pod, namespace, node, and workload details quickly from a local cache, and it can attach them to everything that passes through.

The placement of the Collector matters. When a Collector runs on the same node as the applications, as a node-level agent, it receives data directly from local pods and can match the sender’s network address to a pod with confidence. When a Collector sits in a central gateway, the original sender addresses may have been hidden by earlier network hops, and address-based matching can become unreliable. For this reason, many designs enrich at the node-level agent and apply shared policy, such as masking and renaming, at the gateway.

Collectors can also remove attributes. If an outside vendor does not need internal host names, a processor can delete them before the data leaves your network. Collectors can rename attributes, so that an older name is converted to the current standard. And they can add fixed values, such as the name of the cluster or the company’s cost center, so that applications never have to know them. This central control is among the strongest reasons to place a Collector between your applications and your backends.

9Resources in Kubernetes, the Cloud, and Serverless

Where your program runs decides which resource attributes are available and where they come from.

The same program can run in very different homes. On a developer’s laptop it has a host name and a process number. On a virtual machine it also has cloud details. In Kubernetes it lives in a pod, inside a namespace, on a node. In a serverless platform it exists only while a function runs. Each home has its own natural resource attributes, and its own best way of discovering them.

EnvironmentNatural attributesUsual source
Laptop or plain serverhost.name, os.type, process.pidSDK detectors
Virtual machine in a cloudcloud.provider, cloud.region, cloud.availability_zone, host.idCloud detectors in the SDK or the Collector
Containercontainer.id, container.name, container.image.nameContainer detector or the Collector
Kubernetesk8s.cluster.name, k8s.namespace.name, k8s.pod.name, k8s.deployment.name, k8s.node.nameKubernetes attributes processor, environment variables, or the operator
Serverless functionfaas.name, faas.version, faas.instance, cloud.platformThe function platform and its detectors

Kubernetes habits

In Kubernetes, there are three popular ways to supply the pod-level attributes. The first is the Kubernetes attributes processor in the Collector, described earlier. The second is the platform’s downward API, a feature that lets a pod read facts about itself, such as its own name and namespace, as environment variables. The deployment file can then pass those values into the OpenTelemetry resource variable. The third is the OpenTelemetry Operator, a Kubernetes add-on that can inject instrumentation and set the Kubernetes attributes for you.

Whichever way you choose, it helps to name the cluster in the resource too. A company with several clusters in different regions needs to know that a slow pod in the staging cluster is not a slow pod in the production cluster. Cluster names are not usually discovered automatically, so teams add them by hand or through a shared setting.

Cloud habits

Cloud detectors ask the platform’s own information service, which every virtual machine can reach, about its provider, platform, region, zone, and account. Using a detector is nearly always better than typing these values by hand, because detectors stay correct even when a service is moved between zones. Remember that the account identifier is sensitive in some organizations, as the security chapter explains.

Serverless habits

Serverless functions are special. A function may live for milliseconds, and a platform may run thousands of copies at once. The resource therefore describes the function name, version, and the running instance, and it relies heavily on the platform to supply the right values. Because functions start often, detectors that are slow to run can add noticeable delay. Keep the set of detectors small for functions, and prefer values passed in by the platform.

Everyday Analogy

Think of how you would describe your home address in different situations. To a neighbor you say “the blue house next door.” To a courier you give a street and a number. To an airline you give a country and a city. The facts are the same, but the useful level of detail changes with the audience. A resource also describes the same program in different layers: process, host, container, pod, cluster, and cloud.

i
Be Consistent Across Environments

Use the same attribute names on your laptop, in testing, and in production. A developer who sees the right labels on a local machine is more likely to notice when they are missing in the cluster.

10Resources and Metrics Backends

Metrics systems treat labels with great care, because every new label value can multiply the cost.

Traces and logs usually store resource attributes as extra details beside each record, which is generous and cheap. Metrics systems are different. Many of them build a separate series, a line of numbers over time, for every unique combination of labels. A single metric with a few labels can quickly turn into thousands of series. This is why the way resource attributes are converted into metric labels deserves attention.

Take Prometheus, a very popular open source metrics system. Prometheus has its own habits, and OpenTelemetry follows a documented translation when sending metrics there. In that translation, the service name, possibly combined with the service namespace, becomes the job label. The service instance identifier becomes the instance label. And the remaining resource attributes are placed into a special companion metric called target_info, which can be joined to other metrics when needed instead of being copied onto every series.

flowchart LR
    RES["OpenTelemetry resource attributes"] --> J["Service name and namespace become the job label"]
    RES --> I["Service instance id becomes the instance label"]
    RES --> TI["Other resource attributes go into a target_info metric"]
    MP["Metric data point attributes"] --> L["Become ordinary labels on each series"]
    J --> S["Prometheus series"]
    I --> S
    L --> S
    TI -.->|"joined only when needed"| S
Figure 5: How resource attributes are commonly translated into labels for a Prometheus-style metrics system

Follow the arrows. The most important resource attributes become labels on every series, so you can always filter by service and by copy. Everything else about the producer is parked in the target information metric, where it is available for joining but does not multiply the number of series. Ordinary attributes on each data point become regular labels. This design is deliberate: it keeps the details you need for everyday queries close at hand and keeps the rest out of the way.

Why cardinality matters

Cardinality means the number of different values a label can take. A label with only three possible values, like an environment name, has low cardinality. A label with a new value every day, like a pod name in a busy cluster, has high cardinality. When a high-cardinality resource attribute becomes a label on every series, the number of series multiplies, memory use grows, queries slow down, and, for vendors that bill by series, costs rise.

Resource attributeCardinalityUsually safe as a metric label?
service.nameLow and stableYes
deployment.environmentVery lowYes
cloud.regionLowYes
service.versionGrows slowly with each releaseOften yes, but watch the number of live versions
k8s.namespace.nameLowUsually yes
k8s.pod.nameHigh, and changes with every restartBe careful; better kept in the companion information metric
process.pidHigh and unstableNo
service.instance.idHigh, but needed to tell copies apartYes in the instance label, with care
Everyday Analogy

Think of a library catalog. If you create a separate shelf for every combination of author, color of the cover, and the date the book was borrowed, you would soon need more shelves than there are books. A few well-chosen sections, such as genre and author, keep the library easy to use. Labels on metrics work the same way: a few carefully chosen ones keep the system manageable, and an unbounded one fills the building.

!
Churn Is the Hidden Cost

It is not only the number of series alive right now that matters. Every time a pod restarts and gets a new name, a whole new set of series is born, and the old set dies. Over weeks, this churn can leave millions of short-lived series in storage. When you promote unique-per-restart attributes to labels, you are paying for the history of every restart you have ever had.

11Choosing Good Values

A resource is only as useful as the values inside it. A few simple rules make them clear, stable, and searchable.

Setting a resource is easy. Setting it well takes a little thought, because these values will appear in every dashboard, every alert, and every investigation for years. A good name saves minutes each time someone reads it. A bad name wastes hours across the whole company.

Naming the service

The service name should describe what the program does, not where it runs or which release it is. Good names are short, stable, and meaningful to the whole company, such as “checkout,” “inventory,” or “payment-gateway.” Bad names include the machine name, a version number, a date, or an environment, such as “checkout-prod-2-v7.” Those details belong in their own attributes. If the name changes with every release or every deployment, charts break into pieces, and nobody can follow a service over time.

Uniqueness matters too. If two different teams both call their program “api,” their data will be mixed into a single confusing stream. Many companies solve this by agreeing on a naming rule, keeping a shared list of service names, and using the service namespace attribute to separate groups, for example a namespace for the shopping team and another for the payments team.

AttributeA good valueA poor valueWhy
service.namecheckoutcheckout-prod-2-v7The good name describes the job. The poor one hides the environment, copy, and version inside the name.
service.version2.4.1 or a short build labellatestA moving label cannot tell you which release had the problem.
deployment.environmentproductionprod, Prod, PRODUCTION used by different teamsInconsistent spellings split one environment into several.
service.namespaceshoppingteam1A meaningful group is easier to recognize and still makes sense after a reorganization.
service.instance.idA unique, random string for each running copyThe same value for every copyIf copies share the same ID, you cannot tell them apart.
Everyday Analogy

Think about how a school names its classes. A class called “Year 5 Maths” makes sense for years. A class called “Room 12 Tuesday morning with Mr Patel” stops making sense the moment the room, the day, or the teacher changes. A good service name is like the first kind: it describes the purpose and survives changes in the details around it.

Choosing the version

The version attribute is one of the most valuable for debugging. When something breaks, the first question is, “What changed?” If every piece of telemetry carries the release number, the answer is a single click. Use a real, unique label from your build system, such as a version number or a short commit identifier. Avoid vague words such as “latest” or “current,” because they mean different things on different days.

Choosing the environment

Agree on a small set of environment names and use them everywhere. A typical set is development, testing, staging, and production. The point is not which words you choose but that everyone chooses the same ones. When one team says “prod” and another says “production,” every dashboard needs special handling, and every alert has to list both.

Adding your own attributes

You are free to add attributes that are specific to your company, as long as they describe the producer and not a single request. Popular examples are the owning team, the cost center, the tier of the service, or a link to its runbook. Choose clear names with a company prefix, so they never collide with the standard ones, and keep the number small. Every attribute you add will be stored and sent forever, so each one should earn its place.

i
The Bumper-Sticker Test

A good resource attribute fits on a bumper sticker, never changes during the day, and means the same thing to everyone. If an attribute needs a paragraph to explain, or has a different value every hour, it probably does not belong in the resource.

12Security and Privacy for Resources

A resource describes your infrastructure. In the wrong hands, that description is a map.

At first glance, resource attributes look harmless: a service name, a version, a region. But together they form a detailed description of how your systems are built. They reveal the names of internal services, the layout of your clusters, the accounts you use, and sometimes much more. In addition, some detectors can collect more than you intended. A resource is therefore worth reviewing for privacy and security, just like any other data that leaves your network.

RiskExampleRemedy
Secrets in the command lineA process attribute that records the full command used to start the program, which may include a password or a tokenTurn off or mask command-line details, and never pass secrets as command arguments
Secrets in environment variablesSomeone puts a key inside the resource attributes variableKeep secrets in a proper secret store, never in resource settings
Internal structure exposedHost names, cluster names, and internal service names visible to an outside vendorReview what leaves the network, and mask or generalize where needed
Cloud account detailsAccount or subscription identifiers sent to third partiesRemove them in the Collector if the destination does not need them
Personal data in custom attributesAn email address or a user name added as a resource labelDo not store personal data in resources; they are meant for infrastructure facts
Spoofed identityAn untrusted client sends telemetry claiming to be the payments serviceRequire authentication at the Collector, and set or verify the service name centrally for sensitive sources
Over-collection by detectorsA detector that gathers more than the team realizedEnable only the detectors you need, and review their output
Everyday Analogy

Think of the luggage tag on a suitcase. It helps the airline return the bag, which is its purpose. But if you also write your house alarm code on the tag, every stranger who handles the bag can read it. A resource is a luggage tag for telemetry. Put on it what helps identify the producer, and keep everything sensitive off it.

Command lines deserve special care

Some process detectors can record how the program was started. That is useful, but programs are sometimes started with secrets typed straight into the command, which a careless setup would copy into every piece of telemetry. If you cannot be sure that your command lines are clean, turn this feature off, or mask it in the Collector before the data leaves your network.

Trusting the identity on incoming data

The resource says who produced the data, but anyone who can reach your Collector can write any resource they like. If your systems rely on the service name to decide where data goes, who owns it, or who may see it, then a forged service name could cause trouble. Protect the entrance with authentication and encryption, and, where it matters, set or check the identity at a central point. A gateway Collector is a natural place to do this, because it sees all incoming data.

One place to clean

Asking every team to remember every privacy rule is fragile. A better approach is to put masking rules in a central Collector, so that even a careless application cannot send a secret to an outside vendor by accident. Combine this with reviews of the resource whenever a new detector is turned on or a new custom attribute is added.

!
Review Before You Export

Take a sample of the resource that your applications actually send, and read it as a stranger would. Ask what it reveals, and whether the destination truly needs it. A quick review at the start of a project is far cheaper than discovering a leak later.

A Short Privacy Checklist

Before you enable a new detector or add a custom attribute, walk through a brief checklist. Ask what the attribute reveals about your systems and whether the destination needs to see it. Ask whether it could contain a secret, a personal identifier, or an internal address. Ask who will be able to read it after it arrives, including support staff at outside vendors and engineers in other departments. Ask how long it will be stored, and whether it can be deleted if it turns out to be a mistake.

Consider also the regulations that apply to your business. Personal data is often covered by strict rules about collection, storage, and sharing. Resource attributes are meant for facts about machines and software, so they should rarely contain personal data at all. If a team feels tempted to put a user name or email address into a resource, that is a signal to stop and find a different design, such as a hashed or random identifier that cannot be traced back to a person without extra permission.

Finally, remember that the safest data is data that was never collected. Every attribute you do not send is an attribute that cannot leak. Keep the base set small, review additions with care, and apply masking at a central point so that a single forgotten setting in a single application does not become a company-wide problem.

13Monitoring and Checking Your Resources

Resources fail quietly. Nothing crashes when a label is missing; your dashboards simply become less useful.

If a service name is missing, the application keeps working and the telemetry keeps flowing. The only symptom is that the data appears under an unhelpful label, and someone, some day, cannot find what they need. Because the failure is silent, you must look for it on purpose. A short routine of checks can keep resources healthy.

What to checkWhat it tells youTypical action
Count of telemetry under placeholder service namesPrograms whose set-up is unfinishedFind the owners and ask them to set a real service name
Share of telemetry with Kubernetes labelsWhether enrichment is working across the clusterIf it drops, check permissions and the enrichment processor
Missing environment labelData that cannot be separated into production and testingFix deployment settings so every program sets it
Number of different values per attributeHidden cardinality problems, such as a label with thousands of valuesRemove or move high-cardinality labels
Spelling variationsDifferent teams using different names for the same thingAgree on a standard and correct the stragglers
Number of live versions of one serviceWhether old releases still linger in productionFinish rollouts and retire old copies

A simple health dashboard

Build one dashboard for the platform team with a handful of panels: how many services report, how many use placeholder names, how many telemetry items carry each key attribute, and how many distinct values each important attribute has. The goal is not to look at it every day, but to make problems visible during the regular review, before they hurt anyone.

Check after every change

1

Deploy a new service or change a setting

Any new service, new detector, or new Collector rule is a chance for the resource to change.

2

Send one test request

Create a little telemetry on purpose, so that something fresh is waiting for you to inspect.

3

Read the resource

Open the data in your tool, or print it with a debug exporter, and read every attribute as if for the first time.

4

Compare with the standard

Check that the names match your company’s agreed list and the semantic conventions.

5

Look at the cost

Confirm that no new high-cardinality attribute has crept into your metric labels.

Listen to the SDK

If a detector fails, for example because it cannot reach the cloud information service, the SDK may log a warning and carry on without those attributes. These warnings are easy to overlook. Make sure that diagnostic messages from the SDK are collected somewhere, because they often explain why an expected label is missing.

i
Make the Check Automatic

Add a simple automatic test to your deployment process that starts the service, captures its resource, and fails if the service name is a placeholder or the environment is missing. A thirty-second test can prevent months of confusion.

Turning Checks into Alerts

Checks that nobody reads are only decoration. Choose a few that matter and connect them to a simple alert with a calm, clear message. For example, if the share of telemetry carrying a placeholder service name rises above a small threshold, notify the platform team during working hours, because this is a quality problem and not an emergency. If the number of distinct values for an important label suddenly jumps, notify the owners quickly, because a runaway label can raise costs within hours.

It also helps to track trends instead of single numbers. A slow rise in the number of distinct instance identifiers might be perfectly normal for a growing service, while a sudden jump after a deployment points to a mistake. Compare each service with its own history, and compare similar services with each other, to spot those that behave differently from their peers.

Finally, give every check an owner. A dashboard without an owner slowly fills with outdated panels, and an alert without an owner is eventually muted. Assign the platform team to look after the overall health, and ask each service team to keep its own resource clean. When both sides know their role, problems are fixed while they are small.

14Benefits, Drawbacks, and Trade-offs

Every design has a price. An honest view helps you decide how much to put in your resources.

Advantages

  • Context for everything. Every trace, metric, and log can be traced back to its producer.
  • Efficient. The resource is sent once per batch, not once per item.
  • Standardized. Agreed names let dashboards and alerts work across teams and tools.
  • Mostly automatic. Detectors and processors discover most of the details for you.
  • Vendor-neutral. The same labels work with any tool that understands OpenTelemetry.
  • Great for investigations. Version, region, and pod labels turn guesses into quick answers.

Disadvantages

  • Silent failures. Missing or misspelled labels do not cause errors, only confusion.
  • Immutable once started. You cannot fix a wrong value without a restart.
  • Cardinality risk. Careless labels can multiply metric costs.
  • Privacy risk. Detectors and custom labels can expose more than intended.
  • Naming discipline needed. Without company-wide agreement, labels drift and fragment.
  • Evolving standards. Some names change between versions of the conventions.

Common trade-offs in plain language

ChoiceWhat you gainWhat it costs
Detect attributes automaticallyLess manual work and fewer typing mistakesLess control, and possible over-collection
Set attributes by hand or by environment variableFull control and clear ownershipNeeds discipline, and can drift out of date
Enrich in the applicationWorks even without a CollectorApplications need extra permissions and code
Enrich in the CollectorApplications stay simple, and rules are centralOne more component to run and keep correct
Many resource attributesRich context for investigationsMore data stored and a larger privacy surface
Few resource attributesSmall, clean, cheap dataLess to go on when something breaks
Promote attributes to metric labelsEasy filtering in queriesMore series and higher cost
Keep attributes in a companion metricLower cost and lower series countRequires a join in some queries
i
A Fair Rule of Thumb

Start with a small, standard set: service name, version, environment, and instance, plus whatever your platform provides automatically. Add more only when someone can name the question that the new label will help answer.

15Good Habits and Design Patterns

Patterns are tried-and-true recipes. Habits are the small routines that keep your resources clean, cheap, and useful.

Pattern

A standard base set

Agree on a short list of attributes that every service must send: name, namespace, version, environment, and instance. Everything else is optional.

Pattern

Set it from outside

Let the platform team supply labels through environment variables in deployment files, so developers do not have to remember them and code does not change.

Pattern

Platform owns the infrastructure labels

Applications set service labels. The platform adds cluster, node, pod, and cloud labels through detectors and the Collector. Ownership is clear, and nothing is set twice.

Pattern

Enrich at the agent

Run a small Collector on each node to add pod and host details, where it can see true sender addresses. Let a central gateway apply shared rules.

Pattern

A naming registry

Keep one shared list of service names, namespaces, and approved custom attributes, so new teams copy a known good pattern instead of inventing a new one.

Pattern

Version everything

Stamp every release with a real version label. Comparing the same service across two versions is among the fastest ways to find the cause of a regression.

Habits that pay off

  • Always set the service name. It is the single most valuable attribute. Never ship with the placeholder.
  • Use the semantic conventions. Prefer standard names to inventing your own, so tools and colleagues understand your data.
  • Keep the resource small and stable. Describe the producer, not the request.
  • Turn on only the detectors you need. Each one adds data, startup time, and privacy surface.
  • Decide who owns each attribute. Write it down, so two components never fight over a value.
  • Protect metric systems from high cardinality. Keep unique-per-restart labels out of metric labels unless you truly need them.
  • Review the resource after changes. Read what you actually send, not what you meant to send.
  • Test in a safe place first. Print telemetry with a debug exporter before pointing it at an expensive vendor.
  • Document your standard. A one-page guide with examples makes the right way the easy way.

A gentle path to adopting resource standards

1

Audit what exists

Look at the resource attributes that services send today, and list the problems: placeholders, spelling variations, missing environments.

2

Write the base standard

Choose the small required set, and write down names and examples.

3

Automate the easy parts

Use environment variables, detectors, and Collector processors to fill in what teams should not have to type.

4

Add checks to deployment

Fail builds that would ship without a real service name or environment.

5

Review regularly

Look at the health dashboard each month and tidy up stragglers before they multiply.

i
Make the Easy Path the Right Path

If setting a correct resource takes one line in a shared template, everyone will do it. If it takes a long document and three approvals, people will skip it. Aim to make the correct setup the lowest-effort setup.

Keeping the Standard Alive

Writing a standard is the easy part. Keeping it alive over years of change is harder. Teams grow, reorganize, and adopt new technologies, and each change tempts people to invent new names. To keep the standard useful, treat it like a small product. Give it an owner, publish it somewhere easy to find, show examples for the most common platforms, and welcome requests for changes through a simple, quick process.

Provide shared building blocks, such as a deployment template or a starter library, that already contain the right settings. When creating a new service is as easy as copying the template, the standard spreads without effort. When the semantic conventions change, update the shared building blocks first, and let teams receive the new names through ordinary upgrades rather than through a long manual campaign.

It is also wise to plan for renames. When a name is retired in favor of a new one, send both for a while, then remove the old name once dashboards and alerts have moved. A Collector processor can copy values from the old name to the new one during the transition, so applications can be updated at their own pace. Gentle migrations keep trust high and keep surprises low.

16Common Mistakes, Anti-patterns, and Myths

Most resource problems come from the same few mistakes. Knowing them in advance saves a lot of confusion.

AP-1The Nameless ServiceVery common

What happens: A team ships a program without setting the service name. Everything it sends is filed under the placeholder name, together with every other unfinished program.

Why it hurts: Nobody can tell whose data it is. Alerts cannot be routed, and dashboards show a mixed-up pile.

Better approach: Make the service name a required setting in your deployment template, and add an automatic check that fails when the placeholder appears.

AP-2The Per-Request ResourceCostly

What happens: Someone puts a user identifier, a request number, or a timestamp into the resource, because it seemed like a convenient place.

Why it hurts: A resource is supposed to describe the producer and stay the same for its whole life. Values that change per request belong to a span, a log record, or a metric label, and even there they must be chosen carefully.

Better approach: Ask, “Does this stay true for as long as the program runs?” If not, it does not belong in the resource.

AP-3The Label ExplosionExpensive

What happens: Every resource attribute, including the unique instance and pod names, is copied into every metric label.

Why it hurts: The number of metric series multiplies with every restart, and the bill and the query time grow with it.

Better approach: Keep only the stable, low-cardinality attributes as labels. Let the rest live in the companion information metric and join only when needed.

AP-4Everyone Spells It DifferentlyQuietly harmful

What happens: One team writes “prod,” another “production,” a third “Production.” Nothing is wrong with any single one.

Why it hurts: Queries, alerts, and reports must list every spelling, and someone always forgets one.

Better approach: Publish an approved list of values, and make the template supply them so that nobody types them.

Myths worth clearing up

MythReality
“A resource is the same as a span attribute.”A resource describes the producer and is shared by everything it sends. A span attribute describes one operation.
“Each trace has its own resource.”The resource belongs to the producer, and many traces share it.
“I can change the resource while the program runs.”It is fixed when the provider is created. A change means a restart, or a new provider.
“The Collector cannot change the resource.”Collector processors can add, change, or remove resource attributes in flight.
“More attributes are always better.”Extra attributes cost storage, add privacy risk, and can inflate metric costs.
“Resources are only for traces.”Traces, metrics, and logs all use the same resource.
“The service name is optional.”Without it, the data gets a placeholder name and becomes hard to use.

17A Worked Example: The Checkout Service

Let us follow one imaginary service from a blank page to a fully described, cost-aware resource, using simple numbers.

Imagine an online shop with a service called “checkout.” It runs as 200 copies (pods) inside a Kubernetes cluster in the cloud. The team wants every trace, metric, and log from this service to be easy to find, easy to compare between releases, and affordable.

Step 1: decide who supplies what

The team lists the attributes they want and decides who is responsible for each one, so that nothing is set twice and nothing is forgotten.

AttributeExample valueWhere it comes fromWhy that source
service.namecheckoutDeployment settings (environment variable)Only the team knows the service’s purpose, and it must never be left to chance.
service.namespaceshoppingDeployment settingsShows which group owns the service.
service.version2.4.1Build pipeline, passed into deployment settingsThe build knows the exact release.
service.instance.idA random unique stringThe SDK, generated at startupEach copy must be distinguishable.
deployment.environmentproductionDeployment settingsSet from the approved list.
k8s.pod.name, k8s.namespace.nameNames of the running pod and its groupKubernetes downward API or Collector enrichmentThe platform knows these, and the app should not guess.
k8s.cluster.nameThe cluster’s nameCollector processorOne central place, applied to everything.
cloud.provider, cloud.regionThe cloud and regionCloud detectorDiscovered automatically from the environment.
telemetry.sdk.*SDK name, language, versionThe SDKFilled in automatically.

Step 2: count the cost

The team exports 40 different metrics. Each metric has about 30 combinations of ordinary labels, such as route and status code. Let us see what happens with two different choices.

1,200
Series when only service.name is added (40 × 30)
240,000
Series if every pod name becomes a label (40 × 30 × 200)
7,200,000
Series after 30 days of daily restarts (40 × 30 × 6,000)

Here is how those numbers are built. With only the service name added, each of the 40 metrics has 30 label combinations, so the total is 40 × 30 = 1,200 series. If the pod name is added as a label, every one of the 200 pods gets its own copy of each series: 40 × 30 × 200 = 240,000. Now suppose the pods restart every day and each restart creates a new unique pod name. Over 30 days, 200 pods × 30 days gives 6,000 distinct pod names that appeared at some point, and the total grows to 40 × 30 × 6,000 = 7,200,000 series. The old series are no longer receiving data, but many systems keep them in their index for a long time.

Everyday Analogy

Imagine a library that must keep one card for every book and also one card for every time a book was borrowed. If it only keeps cards for books, the catalog stays small. If it keeps cards for every borrowing, the drawers overflow within months. The pod name is like the borrowing record: it changes all the time, and it belongs in a separate place from the stable catalog.

Step 3: make the decision

The team keeps service name, namespace, version, and environment as metric labels, because they are stable and few. They leave pod names and instance identifiers in the companion information metric, where they appear as one small record per instance instead of multiplying across every series. When an engineer needs to know which pod produced a spike, they join the two with a single query.

Step 4: verify

The team starts one pod in a test environment, prints its telemetry with a debug exporter, and reads the resource attribute by attribute. The service name is correct, the version matches the build, the environment is spelled from the approved list, and the cloud and cluster details are present. Only then do they roll out to all 200 copies. A week later, the health dashboard shows no placeholder names and a steady series count, which is exactly what they wanted.

i
What This Example Teaches

The same set of facts can cost 1,200 series or more than seven million. The difference is not the data you collect but where you place it. Resources are powerful because they are shared, and careful placement keeps that power cheap.

What the Team Learned Afterwards

Looking back, the checkout team noticed that their most valuable decisions were also their simplest. They chose a clear service name and kept it fixed. They stamped every release with a real version. They agreed on environment names and enforced them through the template. They let the platform fill in Kubernetes and cloud details, so developers never typed them. And they kept unique-per-restart values out of metric labels, which saved them from a bill and a slowdown that would have arrived slowly and then all at once.

They also learned to treat the first minute after a deployment as important. A quick look at the resource of a freshly started pod, compared with the standard, catches nearly every mistake before customers are affected. Over time they turned that quick look into an automatic test that runs in the deployment pipeline, and the number of mislabeled services fell almost to zero.

Finally, they wrote a one-page guide for new teams. It listed the required attributes, showed where each comes from, explained the metric cost rule with the same simple numbers used in this example, and pointed to a contact person for questions. New services now start correct by default, which is the best sign that a standard is working.

18Real-World Scenarios

Three short stories show how good resources turn confusion into quick answers.

Scenario 1: The slow release

On a Monday morning, customers report that checkout feels slow. The on-call engineer opens a trace chart and groups it by the version attribute. Version 2.4.1 is fast. Version 2.4.2, which was rolled out to a quarter of the pods on Friday, is clearly slower. In under five minutes the team has found the release responsible and rolls it back. Without the version in the resource, they would have spent the morning comparing guesses.

Scenario 2: The one unhappy region

Error rates rise, but only for some users. An engineer groups the errors by cloud region and finds that nearly all of them come from a single region, while the others look healthy. Because the region is part of every resource, no special search was needed. The team contacts the cloud provider with precise details, and meanwhile shifts traffic away from the affected region.

Scenario 3: The mystery pile

A new platform engineer notices that the data under the placeholder service name is growing every week. Using the resource, which still carries the host and process details, she traces the unlabeled data to a small batch job that nobody had finished configuring. She asks the owning team to set a service name, adds a deployment check so it cannot happen again, and the mystery pile disappears.

i
The Pattern Behind the Stories

In each story, the resource did not solve the problem by itself. It made the right question easy to ask: which version, which region, which service? That is its real value.

Choosing Where to Begin

If your organization is only starting out, you do not need to solve everything at once. Begin with the single most valuable step: make sure every service sets a real service name and a version. Within a few weeks, you will already be able to compare releases and route alerts. Next, add the environment and namespace, so that testing and production are never confused. After that, switch on detectors and Collector enrichment for host, cloud, and Kubernetes details, and finally review cardinality and privacy once the basics are stable.

Each step is small, brings a clear benefit, and can be checked on its own. This gradual path also builds goodwill. Teams see the benefits early, such as faster investigations and cleaner dashboards, and they become willing partners in later, more demanding steps. The resource is a humble concept, but by building it carefully you give every future trace, metric, and log a name, an address, and a story.

19Frequently Asked Questions

Q1What is a resource in one sentence?
A resource is a fixed set of labels that describes the program, machine, and environment that produced the telemetry, attached to every trace, metric, and log it sends.
Q2Is the resource the same as an attribute on a span?
No. A span attribute describes one operation, such as one request. A resource attribute describes the producer and is shared by everything that producer sends.
Q3Which attribute is the most important?
The service name. It is the main label for finding and grouping data. If it is missing, the data is filed under a placeholder name, which is hard to use.
Q4Can I change a resource after the program has started?
Not in the normal way. The resource is fixed when the provider is created. To change it, restart the program. Collectors, however, can modify the resource of data that passes through them.
Q5Where should I set the service name?
Usually in deployment settings, such as an environment variable provided by your template. This keeps the name out of the code and lets the platform team enforce a standard. If the dedicated service name variable and the general resource attributes variable both set it, the dedicated one wins.
Q6What is a resource detector?
A small helper that discovers facts about the environment, such as the host, the process, the container, or the cloud, and turns them into resource attributes so that nobody has to type them.
Q7What happens if two sources set the same attribute?
The sources are merged, and the one that is merged later wins. Because the order matters, decide who owns each attribute and write it down.
Q8Should I add the resource to every log line myself?
No. The logging provider carries the resource and attaches it when the log records are exported. Your messages stay clean.
Q9How do resources appear in Prometheus-style systems?
The service name, and its namespace if present, usually become the job label, and the instance identifier becomes the instance label. Other resource attributes are commonly placed in a separate information metric, which you can join in queries when needed.
Q10Is it safe to include the command line of my process?
Only if you are certain no secrets appear in it. Many teams turn this off or mask it in a Collector, because command lines sometimes contain passwords or tokens.
Q11Do I need a Collector to have good resources?
No. Applications can set and detect their own resources. A Collector adds convenience and central control, especially for adding Kubernetes details, applying shared rules, and cleaning sensitive values.
Q12Do the attribute names ever change?
Yes, occasionally. The semantic conventions evolve, and some names have been renamed over time. Check the current conventions when you set up a new service, and plan a gentle migration if an old name is retired.

20Summary and Key Takeaways

A resource is a small idea with a very large effect: it tells every signal where it came from.

We started with a simple problem: a stream of telemetry with no sender’s name on it. We saw how a resource solves it by describing the producer once and sharing that description with every trace, metric, and log. We looked at where its attributes come from, how they are merged, how they travel inside the data, how Collectors enrich them, how metric systems translate them, and how to choose good values. We also looked at the dangers: silent failures, high cardinality, and sensitive details leaking out. The cure for all of them is the same: a small, standard, well-owned set of labels, checked regularly.

Key Takeaways

  • A resource describes the producer. It answers who, where, and which version, and is shared by all traces, metrics, and logs from that producer.
  • service.name is the one to get right. Without it, data lands under a placeholder name and becomes hard to find or route.
  • Keep it stable and small. Resources are fixed at startup; put request-level details in spans, logs, or metric labels instead.
  • Let the right source supply each attribute. Teams set service details, detectors and the platform supply infrastructure details, and Collectors enrich centrally.
  • Watch cardinality. Unique-per-restart values such as pod names can multiply metric series from 1,200 into millions.
  • Treat resources as sensitive. Mask command lines, avoid secrets and personal data, and authenticate who may send telemetry.
  • Check it regularly. Add automatic checks and a small health dashboard, because resource problems fail silently.