Suggest an editImprove this articleRefine the answer for “What are Docker labels and what are they used for?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Docker labels** are key-value metadata you attach to images, containers, volumes, networks, or services. They do nothing on their own — but tools (Traefik, Watchtower, Compose, monitoring) read them to decide behavior. ```dockerfile LABEL org.opencontainers.image.source=https://github.com/myorg/myapp LABEL maintainer="team@example.com" ``` ```bash docker run --label env=prod --label team=platform -d myapp docker ps --filter label=env=prod ``` **Key:** labels are how Docker objects carry metadata. Use OCI-standard keys (`org.opencontainers.image.*`) for image metadata. Tool-specific keys (`traefik.*`, `com.docker.compose.*`) drive automation.Shown above the full answer for quick recall.Answer (EN)Image**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.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.