skip to content

Annotations & Metadata

Annotations hold non-identifying data controllers read, from an ingress tweak to the last-applied config kubectl diffs against, while uid, generation and resourceVersion are ObjectMeta fields the server fills in. 'Label or annotation?' is the fast check.

part ofKubernetesoverview, primer and where to startread it →
on this pageshow

questions

3

In Kubernetes, what is the difference between a label and an annotation, and how do you decide which one to use?

level: juniorimportance: must knowfreq 78%

answer

  1. identify versus describe
  2. which one can a selector match
  3. 63-character value limit on one side
  4. 256 KiB total on the other
  5. kubectl apply stores its manifest there

basics

~20 s

Labels are short identifying key/value pairs that selectors match, so Services, Deployments and kubectl -l find objects by them. Annotations carry non-identifying data of any shape, capped at 256 KiB in total, that tools read by key but nothing can select on.

solid answer

~50 s

Both are string maps in `metadata` and share the same key syntax: an optional DNS-subdomain prefix plus a name of up to 63 characters. **Labels identify**: values are capped at 63 characters from a restricted character set, and they are what selection works on — a Service's selector, a Deployment's `.spec.selector` and `kubectl get -l` all match labels. **Annotations describe**: a value can be any string, including multi-line JSON, limited only by 262144 bytes for all keys and values together, and no selector can query them. Tools and controllers read them by key — `kubectl apply` stores `kubectl.kubernetes.io/last-applied-configuration`, and the Deployment controller writes `deployment.kubernetes.io/revision`. The fast check: if anything will ever need to *find or group* objects by the value, it is a label; if it is information *about* the object, such as a build commit, an on-call contact or a tool setting, it is an annotation.

code

yaml · 27 lines
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: game-session
  labels:
    app.kubernetes.io/name: game-session
    games.example.com/store-tier: flagship
  annotations:
    games.example.com/build-commit: "9f2c41e"
    games.example.com/oncall: "store-edge-team"
    games.example.com/build-log: "https://ci.example.com/builds/5827"
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: game-session
  template:
    metadata:
      labels:
        app.kubernetes.io/name: game-session
    spec:
      containers:
      - name: server
        image: registry.example.com/game-session:2.14.3
        resources:
          requests:
            cpu: 350m

go deeper

for a junior

Recall the split: labels identify and can be selected, annotations describe and cannot. Name one example of each, such as app.kubernetes.io/name and a build commit.

for a middle

Explain the constraints behind the split: 63-character constrained label values versus unrestricted annotation values under a 262144-byte total, and name built-in annotations like last-applied-configuration and deployment.kubernetes.io/revision.

for a senior

Show you have seen annotations fail in production: the size limit hit through client-side apply, and typo'd controller annotations silently ignored because nothing validates them.

for a principal

Frame labels as a shared, governed vocabulary the whole platform selects on, and annotations as per-tool configuration channels that need naming prefixes and ownership conventions to stay manageable.

## Two maps in every object's metadata Every persisted Kubernetes object has a `metadata` block (the **ObjectMeta** type). Two of its fields are free-form string-to-string maps that you, rather than the API server, fill in: - `metadata.labels` — **identifying** attributes. - `metadata.annotations` — **non-identifying** attributes. They look almost identical in YAML, which is why interviewers ask how they differ. The difference is not the syntax; it is what the API server lets you do with them. ## Labels: small, identifying, selectable A **label** says what an object *is* in terms other objects can query. The API server supports **label selectors** on list and watch requests, so labels are the glue that lets objects find each other: - a Service picks its backend Pods by labels; - a Deployment's `.spec.selector` decides which Pods and ReplicaSets it owns; - `kubectl get pods -l app.kubernetes.io/name=game-session` filters by them. Because they are matched constantly, labels are deliberately **small and constrained**. A value is at most 63 characters, may be empty, and must start and end with an alphanumeric character with only `-`, `_` and `.` in between. Spaces, slashes and JSON are rejected. ## Annotations: arbitrary, non-identifying, never selected An **annotation** records information *about* an object that no selector will ever match on. The key follows the same rules as a label key, but the **value is unrestricted**: free text, a URL, a multi-line JSON document. The only size rule is a **total limit of 262144 bytes (256 KiB)** across all annotation keys and values on one object; a write over that is rejected with `metadata.annotations: Too long: must have at most 262144 bytes`. No selection is possible on annotations: there is no annotation-selector parameter on list requests, and no Service or workload can target Pods by one. Annotations are read **by key** — a component fetches the object and looks up the entry it cares about. ## Side by side | Aspect | Labels | Annotations | |---|---|---| | Purpose | Identify and group objects | Attach descriptive or tool data | | Value size | At most 63 characters | Any length, 262144-byte total per object | | Value characters | Alphanumerics plus `-`, `_`, `.` | Any string, including JSON | | Selectable | Yes — Services, workload selectors, `kubectl get -l` | No | | Typical readers | Controllers, the scheduler (via selectors), humans filtering | kubectl, controllers, CD tools, humans reading | | Key syntax | Optional DNS-subdomain prefix + `/` + name of at most 63 chars | Same as labels | Prefixes `kubernetes.io/` and `k8s.io/` are reserved for Kubernetes' own components; your keys should use a domain your team owns, such as `games.example.com/build-commit`. ## Annotations Kubernetes writes itself Seeing the built-in ones makes the purpose concrete: 1. `kubectl.kubernetes.io/last-applied-configuration` — client-side `kubectl apply` stores the full manifest you last applied here, as JSON. It is far too large and too unstructured to be a label. 2. `deployment.kubernetes.io/revision` — the Deployment controller numbers each rollout on the ReplicaSet and mirrors the current number onto the Deployment. 3. `kubectl.kubernetes.io/restartedAt` — `kubectl rollout restart` stamps a timestamp into the pod template. 4. `kubernetes.io/change-cause` — a human-written note that `kubectl rollout history` prints per revision. Ingress controllers, CD tools and operators use the same channel for their own settings. Because annotations are **not validated against any schema**, a mistyped key is simply ignored by whatever was meant to read it. ## Choosing in practice Take a **multiplayer game-session backend** running on a **5-node edge cluster in a retail store**, each pod requesting **0.35 core** (`cpu: 350m`). The team wants to record several facts on its Deployment: - the component name, `app.kubernetes.io/name: game-session` — a **label**, because the Service and the Deployment selector must find these pods; - the store tier, `games.example.com/store-tier: flagship` — a **label**, because dashboards and `kubectl get -l` will group by it; - the Git commit `9f2c41e` and a link to the build log — **annotations**, because nobody selects by a commit hash and a URL is not a legal label value; - the on-call contact and a 300-character JSON feature-flag summary — **annotations**, far past the 63-character label limit. A useful rule: labels answer *which ones?*, annotations answer *what about this one?*. Putting descriptive data into labels bloats every selector-indexed object and breaks on the character rules; putting identity into annotations means nothing can ever select on it.

  • What happens when an object's annotations grow past the size limit, and where does that usually bite?
    The API server rejects the write with `metadata.annotations: Too long: must have at most 262144 bytes`. The common trigger is client-side `kubectl apply` on a large ConfigMap: apply copies the whole manifest into `kubectl.kubernetes.io/last-applied-configuration`, so a ConfigMap with a few hundred KiB of data fails even though the ConfigMap itself is within its own limit. Creating or replacing it instead, or switching to server-side apply, avoids storing that copy.
  • Why does `kubectl annotate` refuse to change an annotation that already exists?
    `kubectl annotate` protects existing keys: setting a key that is already present fails unless you pass `--overwrite`, so a script cannot silently clobber a value another tool wrote. Appending a dash to the key, as in `games.example.com/build-commit-`, removes the annotation.
  • Who may use the `kubernetes.io/` and `k8s.io/` key prefixes?
    Those prefixes are reserved for Kubernetes core components and documented well-known keys. Your own labels and annotations should carry a prefix from a DNS domain your team controls, such as `games.example.com/`, so they cannot collide with keys the project or other tools define.

Labels are the colour-coded tags on warehouse shelves that pickers scan to find items; annotations are the packing slip taped inside the box, read only by whoever opens that particular box.

saying these in an interview costs you the question

  • A Service selector can match annotations just like labels.
  • Labels are the right place for long descriptions or JSON blobs.
  • Annotations are only comments that no Kubernetes component ever reads.
  • Label values can be any length as long as the object fits in etcd.
  • Every single annotation value has its own 63-character limit.
open as a page

In Kubernetes ObjectMeta, what do the server-populated uid, generation and resourceVersion fields each tell you, and what does generateName do?

level: middleimportance: should knowfreq 42%

basics

~20 s

uid identifies one incarnation of an object, so delete-and-recreate yields a new one. generation counts desired-state changes, resourceVersion changes on every stored write, and generateName makes the server append a random suffix to a name prefix.

open as a page

A team annotates a Kubernetes Deployment expecting its game-session pods to restart, but nothing rolls out. Why, and which annotations actually drive a Deployment's rollouts and revision history?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Only a change to the pod template starts a rollout, so annotations on the Deployment's own metadata never restart pods. Annotations under spec.template.metadata do, which is how kubectl rollout restart works, while deployment.kubernetes.io/revision numbers each rollout.

open as a page