OpenTelemetry Kubernetes Attributes Processor v1 Migration: Seven Renames, On by Default

Going stable renamed seven Kubernetes attributes and turned the change on by default, while the deprecation warning that would have told you never fired. The audit to run, and the one flag that buys time.

By VVV Ops ·

Seven attribute names changed when the OpenTelemetry Kubernetes attributes processor went stable, and the change arrived switched on. The v0.161.0 contrib release, published in September 2026, promoted the processor's logs, metrics and traces signals from beta to stable and promoted both of its semantic-convention feature gates from alpha to beta. A gate at beta is enabled by default. So a routine Collector bump moves your Kubernetes labels and annotations to new attribute names, and every dashboard, alert and recording rule still asking for the old ones gets nothing back.

We have written before about cutting telemetry volume inside the Collector. This post is the other side of the same config file: what to check before an upgrade renames it for you.

The seven names that moved

The processor's semantic conventions compatibility section lists the full set of breaking changes between the two schemas:

| Old (v0 schema) | New (v1 schema) | |---|---| | container.image.tag | container.image.tags | | k8s.pod.labels.<key> | k8s.pod.label.<key> | | k8s.pod.annotations.<key> | k8s.pod.annotation.<key> | | k8s.node.labels.<key> | k8s.node.label.<key> | | k8s.node.annotations.<key> | k8s.node.annotation.<key> | | k8s.namespace.labels.<key> | k8s.namespace.label.<key> | | k8s.namespace.annotations.<key> | k8s.namespace.annotation.<key> |

Six of the seven drop a single letter. Labels and annotations go from plural to singular, which is the shape the Kubernetes resource attributes took when they graduated to stable in Semantic Conventions v1.42.0 in June 2026.

The container image tag is the one that goes the other way, and it is the one worth reading twice. It gains a letter and it changes type. In the processor's own metadata, container.image.tag was a string and container.image.tags is a slice. A query that compared the attribute to a value is now comparing against a list. Anything matching on it, a Grafana variable, a Prometheus relabel rule, a log filter, needs rewriting rather than renaming.

The core identity attributes did not move. k8s.pod.name, k8s.pod.uid, k8s.namespace.name, k8s.deployment.name and k8s.node.name are unchanged, so service maps and most pod-level views survive. The damage is concentrated in the places where someone built a view on a business label: team, cost center, tier, environment.

The warning that was supposed to tell you

Upstream built a deprecation warning for exactly this situation. In v0.161.0 it did not fire.

Issue #51188, opened on 18 September 2026, reports that no warnings are logged in either configuration: not when the defaults silently stop emitting an attribute you asked for, and not when the gates are disabled and the rename is pending. The cause is small and complete. The warning checks read kp.rules during Start(), but kp.rules is populated later in the same function, so the checks always see the zero value and the warning blocks are dead code. It was fixed by PR #51189 the same day.

The reporter's first case is the one to take seriously. List container.image.tag under extract.metadata, upgrade to v0.161.0, and the attribute stops being emitted. The config is still valid. The Collector still starts. The attribute is simply gone, and nothing in the log says so.

If your Collector is already past v0.161.0, the gates are on and the v0 names are already absent from new data. That is no reason to roll back, but it does change the order of work: you are reconciling existing queries against data that has already moved rather than planning ahead of a change.

Find every query that spells the old names

The Collector change is a few lines. The work is in the consumers, and those live outside your infrastructure repository: saved dashboards, alert rules, SLO definitions, recording rules, ingest pipelines, ad-hoc queries someone pinned to a channel.

Grep for the plural forms as literal strings across dashboard JSON, rule files and chart values:

# any of the seven old names, across exported dashboards and rules
grep -rnE 'k8s\.(pod|node|namespace)\.(labels|annotations)\.|container\.image\.tag\b' \
  dashboards/ rules/ charts/

Export what the backend holds rather than trusting the repo. Dashboards edited in a UI and never committed are the ones that break, because they are the ones no longer in anyone's review path. For a Prometheus-backed setup, the rule files and the Grafana dashboard JSON are both reachable through their APIs, and a diff against the grep above tells you how much drift you have.

One more check, cheaper than it looks: confirm the label values themselves agree across clusters. At one client, the dev environment on GKE sent its environment tag as dev while the dev environment on EKS sent development, so the GCP traffic fell outside every environment-scoped alert and dashboard. Our engineer compared the AWS and GCP Terraform, worked out what had to change and whether the platform carried a tag that separates AWS from GCP data, then switched the GCP environment tag from the short name to the full one. Checked later across the whole retention window, both dev environments reported development, 30.4 million entries from EKS dev and 1.6 million from GKE dev, and the value dev no longer occurred anywhere. One problem stayed open: the GKE dev cluster also hosts qa1 and qa2, which report the same value and cannot be told apart.

An attribute rename makes that class of mismatch visible, because you are opening every query anyway. Do the value audit in the same pass. If you are starting from nothing, the label schema we use for GPU chargeback is a reasonable template for which labels deserve to reach telemetry at all.

Run both schemas while you fix the queries

Dual emission is the upstream recommendation and it is one flag. Keep the v0 names flowing by disabling DontEmitV0K8sConventions, and turn on the v1 names at the same time:

--feature-gates=-processor.k8sattributes.DontEmitV0K8sConventions,processor.k8sattributes.EmitV1K8sConventions

The leading minus disables the first gate. Both names then appear on every resource, old queries keep working, and you migrate dashboards and rules at whatever pace review allows. The processor README notes the gates stay at beta for some period while the component remains at v1.x.x, so there is a window rather than an open-ended promise, and no published end date for it.

Two costs come with dual emission. Every renamed label and annotation is carried twice, which raises attribute volume on exactly the dimension that drives metrics cardinality, so treat the window as temporary and give it an owner. And a migration left half-finished on dual emission is worse than either end state, because the next person cannot tell which name is authoritative.

We would set the gates for dual emission before the upgrade lands, fix queries in one sprint, then remove the override and let the defaults take over. Config under Helm looks like this, with the processor under its current name:

# illustrative values for the Collector chart
processors:
  k8s_attributes:
    auth_type: serviceAccount
    filter:
      node_from_env_var: KUBE_NODE_NAME
    extract:
      metadata:
        - k8s.namespace.name
        - k8s.pod.name
        - k8s.pod.uid
        - k8s.deployment.name
        - k8s.node.name
      labels:
        - tag_name: app.label.component
          key: app.kubernetes.io/component
          from: pod
    pod_association:
      - sources:
          - from: connection

The processor key itself has a deprecated spelling

While you are in that file: the processor's declared type is now k8s_attributes, and k8sattributes is recorded as its deprecated type in the component's metadata. The contrib changelog refers to it as processor/k8s_attributes. The feature-gate IDs kept the old spelling, which is why the flag above reads processor.k8sattributes.EmitV1K8sConventions while the config block reads k8s_attributes. The two are not expected to match, so do not try to make them.

Older examples and internal runbooks will carry the compact spelling. It still works, and a deprecated type is a thing to fix on a normal cadence rather than an emergency.

What this costs if you skip it

A missing attribute does not page anyone. An alert whose selector no longer matches any series does not fire, and a dashboard panel with no data looks like a quiet system. That is the failure mode to plan against, and it is the same one behind the environment-tag mismatch above: the monitoring kept reporting, and what it reported was the absence of a problem.

The upstream pieces of this are cheap. One release note, seven names, one feature-gate flag. The expensive part is the inventory of queries nobody has looked at since they were written, and that cost is the same whether you pay it now or after an alert fails to fire. Our Kubernetes production readiness checklist treats observability as a thing with owners for this reason.

If you want a second pair of eyes on the upgrade, tell us what you run. The first thing we would ask for is an export of your alert rules and dashboard JSON, because that is where the seven names are hiding and it is the only part of this migration that cannot be read off a release note.

Tags: opentelemetry kubernetes attributes processor v1 migration, k8sattributes processor feature gates, opentelemetry semantic conventions kubernetes, kubernetes observability attribute rename, opentelemetry collector upgrade breaking changes, kubernetes telemetry labels