Skip to content

07 - Labels and Selectors

What this session is

About 30 minutes. Labels are how Kubernetes finds things. Almost everything in K8s uses them - Services find pods by label, Deployments manage their pods by label, monitoring scrapes pods by label.

Labels

Labels are key=value pairs on resources, set in metadata:

metadata:
  name: nginx-pod
  labels:
    app: nginx
    tier: frontend
    env: production

Add or change labels on a running resource:

kubectl label pod nginx-pod env=staging
kubectl label pod nginx-pod env-           # remove (trailing dash)

Selectors

Selectors query by label. Two forms:

Equality:

kubectl get pods -l app=nginx
kubectl get pods -l env=production,tier=frontend

Set-based:

kubectl get pods -l 'app in (nginx,redis)'
kubectl get pods -l 'env notin (prod)'
kubectl get pods -l 'tier'                  # has the label, any value
kubectl get pods -l '!debug'                # doesn't have the label

Use whichever fits. Equality is shorter; set-based is more flexible.

How K8s components use labels

Most controllers use selectors:

Service finds pods to route to:

kind: Service
spec:
  selector:
    app: nginx

Deployment manages pods matching its template:

kind: Deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx          # MUST match the selector

NetworkPolicy allows/denies traffic to pods matching:

spec:
  podSelector:
    matchLabels:
      app: nginx

HorizontalPodAutoscaler finds pods to scale.

PodDisruptionBudget protects pods from voluntary disruption.

It's the universal "how do I find these things" mechanism. Master it.

Common label conventions

These conventions are widely followed. Use them in your own YAML:

Label Meaning
app.kubernetes.io/name The application's name (e.g. nginx, redis).
app.kubernetes.io/instance A specific install (e.g. nginx-prod).
app.kubernetes.io/version App version.
app.kubernetes.io/component Role (e.g. database, frontend).
app.kubernetes.io/part-of Higher-level app (e.g. wordpress).
app.kubernetes.io/managed-by Tool managing this (e.g. helm).

A pod from a typical Helm chart will have all of these - useful for filtering ("show me all pods that are part of the wordpress app").

Inside a Deployment template, app: <name> (short form) is widely used and that's also fine for most cases.

Annotations vs labels

Labels are for selection. Short, indexed, queryable, limited in size.

Annotations are arbitrary metadata. Free-form, not queryable. Used for: build info, prometheus configs, ingress rules ("rewrite this path"), etc.

metadata:
  labels:
    app: nginx                # for selection
  annotations:
    description: "the main web frontend"
    deployed-by: "ci-build-#1234"
    nginx.ingress.kubernetes.io/rewrite-target: "/"

You set annotations the same way (kubectl annotate ...). They don't drive controller behavior the way labels do.

Real-world: filter by labels

Find everything tagged with app=nginx:

kubectl get all -l app=nginx

Across all namespaces:

kubectl get pods -A -l app=nginx

Show with the labels column visible:

kubectl get pods --show-labels

Show specific label as a column:

kubectl get pods -L app -L tier

These are daily-use commands. The --show-labels flag in particular is useful when debugging "why isn't my Service routing to my pod?" - answer is almost always "the labels don't match."

A common debugging pattern

"My Service has no endpoints." Almost always: the Service's selector doesn't match any pods.

kubectl describe service nginx
# Endpoints: <none>          ← the smoking gun

kubectl get pods --show-labels
# you see pods with app=nginx-app, not app=nginx

Fix one or the other. Pods or the Service selector - make them match.

Exercise

  1. Apply the nginx Deployment + Service from page 04. Confirm endpoints exist:

    kubectl get svc nginx
    kubectl get endpoints nginx
    

  2. Filter by label:

    kubectl get pods -l app=nginx
    kubectl get pods -L app -L tier        # show labels as columns
    

  3. Break selection on purpose:

    kubectl label pod <one-pod> app=wrong --overwrite
    kubectl get endpoints nginx        # endpoints shrunk by one
    
    Fix:
    kubectl label pod <that-pod> app=nginx --overwrite
    

  4. Add an annotation:

    kubectl annotate pod <one-pod> note="this is for debugging"
    kubectl get pod <that-pod> -o yaml | grep -A2 annotations
    

What you might wonder

"What characters can be in label keys/values?" Limited: alphanumeric, -, _, .. Length: ≤63 chars for value, key has a similar limit. If you need to store arbitrary text, use annotations instead.

"Why both labels and tags? (in some clouds)" Different worlds. Cloud tags (AWS, GCP) are on cloud resources. K8s labels are on K8s resources. Some tools sync between them; don't confuse the two.

"What's a 'selector' in a NetworkPolicy?" Same idea - pick pods by labels. NetworkPolicies allow/deny traffic between pods matching the selector. Beyond beginner; you'll meet them eventually.

Done

  • Add labels to resources.
  • Query with selectors (equality and set-based).
  • Recognize the app.kubernetes.io/* convention.
  • Distinguish labels (selectable) from annotations (free-form metadata).
  • Debug "no endpoints" issues by checking labels.

Next: Volumes and storage →

Comments