Skip to main content

What are Docker labels and what are they used for?

Docker labels are arbitrary key-value pairs you can attach to almost any Docker object. They are quiet metadata — Docker itself does not act on them, but the ecosystem of tools around Docker reads them constantly. Knowing how to use labels is what unlocks half of the modern Docker tooling.

Theory

TL;DR

  • Labels = key-value strings attached to images, containers, volumes, networks, services, and Compose stacks.
  • Set in Dockerfile (LABEL), at run time (--label), in Compose (labels:), or via API.
  • Docker does not act on labels — tools that watch the Docker API do.
  • Standard namespaces:
    • org.opencontainers.image.* — OCI image annotations (source, version, license).
    • com.docker.compose.* — set automatically by Compose.
    • traefik.* — read by Traefik to configure routing.
    • org.label-schema.* — older convention, still seen in legacy Dockerfiles.
  • Filter and query: docker ps --filter label=env=prod, docker images --filter label=team=platform.

How to set labels

In Dockerfile

dockerfile
LABEL org.opencontainers.image.source=https://github.com/myorg/myapp LABEL org.opencontainers.image.version=1.2.3 LABEL org.opencontainers.image.description="My production API" LABEL org.opencontainers.image.licenses=MIT LABEL maintainer="team@example.com" # OR multi-line LABEL org.opencontainers.image.source=https://github.com/myorg/myapp \ org.opencontainers.image.version=1.2.3 \ maintainer="team@example.com"

These travel with the image — docker inspect myimg shows them, docker images --filter label=... finds them.

At run time

bash
docker run -d \ --label env=prod \ --label team=platform \ --label cost-center=engineering \ --name api \ myapp:1.0

Labels apply to the container instance, not to the image.

In Compose

yaml
services: api: image: myapp:1.0 labels: env: prod team: platform "traefik.http.routers.api.rule": "Host(`api.example.com`)" "traefik.http.services.api.loadbalancer.server.port": "3000"

Note: dotted keys with values containing dots/special chars need quoting in YAML.

Standard namespaces and what they are for

OCI image labels (standardized)

org.opencontainers.image.created # ISO timestamp of build org.opencontainers.image.authors # contact info org.opencontainers.image.url # documentation URL org.opencontainers.image.documentation # docs URL org.opencontainers.image.source # source repo URL org.opencontainers.image.version # image semver org.opencontainers.image.revision # source git SHA org.opencontainers.image.vendor # publisher org.opencontainers.image.licenses # SPDX license expression org.opencontainers.image.title # human-readable name org.opencontainers.image.description # short description

Product images on Docker Hub, GHCR, and other registries use these. Setting them is best practice for any image you publish.

Tool-driven labels

yaml
# Traefik — auto-routing labels: - "traefik.enable=true" - "traefik.http.routers.web.rule=Host(`api.example.com`)" - "traefik.http.services.web.loadbalancer.server.port=3000" # Watchtower — auto-update labels: - "com.centurylinklabs.watchtower.enable=true" - "com.centurylinklabs.watchtower.scope=production" # autoheal — auto-restart unhealthy labels: - "autoheal=true"

These tools subscribe to Docker events and read labels to decide what to do.

Compose's automatic labels

Compose adds these to every container it creates:

com.docker.compose.project=<project> com.docker.compose.service=<service-name> com.docker.compose.container-number=1 com.docker.compose.config-hash=<sha> com.docker.compose.oneoff=False com.docker.compose.version=<compose-version>

Filter Compose stacks by these:

bash
docker ps --filter label=com.docker.compose.project=myapp

Filtering and querying

bash
# Containers with a specific label docker ps -a --filter label=env=prod # Containers with a label key (any value) docker ps -a --filter label=team # Containers WITHOUT a label docker ps -a --filter "label!=team=platform" # Multiple filters (AND) docker ps -a --filter label=env=prod --filter label=team=api # Same for images, volumes, networks docker images --filter label=org.opencontainers.image.version=1.2.3 docker volume ls --filter label=backup=daily docker network ls --filter label=stack=myapp

Common mistakes

Using labels as state

Labels are metadata, not application state. They do not update at runtime; the Docker API treats them as immutable for the object's lifetime. If you need state, use a database or external store.

Putting secrets in labels

yaml
# WRONG labels: - "db.password=hunter2"

Labels appear in docker inspect, ps -a, and image history. Visible to anyone with read access to the daemon. Never put secrets in labels.

Inconsistent label keys

yaml
# Bad service-1: { labels: { env: prod } } service-2: { labels: { environment: production } } service-3: { labels: { ENV: PROD } }

Filtering becomes a guessing game. Pick one convention (lowercase, no abbreviation, dot-separated for namespacing) and document it.

Forgetting to set OCI labels on published images

Pushing an image to Docker Hub or GHCR without org.opencontainers.image.* labels is a missed opportunity. Registries display them; users rely on them to discover sources, licenses, and version info.

Real-world usage

  • Reverse-proxy auto-routing: Traefik labels make a service's routes self-describing. Add a service to the network with the right labels, Traefik picks it up.
  • Auto-update: Watchtower watches labeled containers and pulls newer images on a schedule.
  • Cost allocation: label every container with team, cost-center, env. Monitoring stack groups metrics by label for chargeback.
  • Backup discovery: label volumes with backup=daily; backup script enumerates by label.
  • Compliance: label images with compliance.scan-date, compliance.cve-count; filter for old or vulnerable images.
  • Multi-tenant cleanup: docker rm $(docker ps -aq --filter label=tenant=acme) to wipe one tenant's containers.

Follow-up questions

Q: Are labels mutable?


A: No. Labels are set at object creation time and persist for the object's lifetime. To change, recreate the object.

Q: What is the difference between Docker labels and Kubernetes labels?


A: Same idea, different scope. K8s labels are first-class for selecting pods/services into deployments and services. Docker labels are passive metadata read by tools. K8s also has annotations (non-selectable arbitrary metadata) which are closer in spirit to Docker labels.

Q: Can I add labels to a running container?


A: No. Labels are set at create time. To add, recreate the container with --label new-key=value.

Q: What is the recommended format for label keys?


A: Reverse-DNS, all lowercase: com.example.team.env. Standard prefixes: org.opencontainers.*, com.docker.*. Avoid uppercase, spaces, and special characters.

Q: (Senior) How would you use labels for a multi-tenant Docker setup with cost allocation?


A: Mandate labels at admission: every docker run (or Compose service) requires tenant, team, cost-center, env. Enforce via a pre-deploy check or admission controller. Monitoring stack (cAdvisor + Prometheus) sees labels via container_label_* metrics; Grafana dashboards group by tenant. Billing pipeline enumerates docker ps --filter label=tenant=... and aggregates resource usage per tenant. Cleanup automation deletes by label scope. Labels are the glue that connect provisioning, monitoring, and accounting.

Examples

Production-quality Dockerfile with OCI labels

dockerfile
FROM node:22-alpine LABEL org.opencontainers.image.title="My API" LABEL org.opencontainers.image.description="Production API for myorg.com" LABEL org.opencontainers.image.source="https://github.com/myorg/myapp" LABEL org.opencontainers.image.version="1.2.3" LABEL org.opencontainers.image.licenses="MIT" LABEL org.opencontainers.image.authors="Platform Team <platform@myorg.com>" LABEL org.opencontainers.image.documentation="https://docs.myorg.com/api" WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . USER node CMD ["node", "server.js"]

GitHub's GHCR displays these on the package page. Docker Hub does the same. Anyone pulling the image can find the source, license, and contact.

Compose stack with Traefik labels for auto-routing

yaml
services: traefik: image: traefik:v3 command: - --providers.docker - --providers.docker.exposedbydefault=false - --entrypoints.web.address=:80 ports: ["80:80", "8080:8080"] volumes: - /var/run/docker.sock:/var/run/docker.sock:ro api: image: myorg/api:1.0 labels: - "traefik.enable=true" - "traefik.http.routers.api.rule=Host(`api.example.com`)" - "traefik.http.routers.api.entrypoints=web" - "traefik.http.services.api.loadbalancer.server.port=3000" - "team=platform" - "env=prod" web: image: myorg/web:1.0 labels: - "traefik.enable=true" - "traefik.http.routers.web.rule=Host(`example.com`)" - "traefik.http.services.web.loadbalancer.server.port=80" - "team=platform" - "env=prod"

Traefik discovers services by labels; team/env labels are for filtering and reporting. No nginx config files, no manual route updates.

Filter and clean by label

bash
# Show all prod containers across stacks docker ps --filter label=env=prod --format 'table {{.Names}}\t{{.Status}}' # Stop everything labeled "team=experiments" docker stop $(docker ps -q --filter label=team=experiments) # Find all images > 1 day old from a specific source docker images --filter label=org.opencontainers.image.source=https://github.com/myorg/oldproject # Compose project enumeration docker ps --filter label=com.docker.compose.project=myapp \ --format '{{.Names}} {{.Status}}'

Labels turn docker ps/docker images into a powerful query tool.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Comments

No comments yet