Labels are key-value metadata attached to containers. They cost nothing at runtime but unlock powerful filtering, organization, and tool integrations.

Adding labels to services

services:
  api:
    image: myapp-api
    labels:
      com.example.team: "backend"
      com.example.env: "production"
      com.example.version: "2.1.0"

  worker:
    image: myapp-worker
    labels:
      com.example.team: "backend"
      com.example.env: "production"
      com.example.role: "async-processing"

You can also use the list syntax:

services:
  api:
    image: myapp-api
    labels:
      - "com.example.team=backend"
      - "com.example.env=production"

Filtering with labels

Labels become powerful with docker compose ps:

# Find all containers from the backend team
docker compose ps --filter "label=com.example.team=backend"

# Find all production containers
docker compose ps --filter "label=com.example.env=production"

# Combine filters
docker compose ps --filter "label=com.example.team=backend" --filter "label=com.example.env=production"

Traefik integration

Traefik uses labels for automatic routing configuration:

services:
  web:
    image: myapp
    labels:
      traefik.enable: "true"
      traefik.http.routers.web.rule: "Host(`app.example.com`)"
      traefik.http.routers.web.tls: "true"
      traefik.http.services.web.loadbalancer.server.port: "3000"

  traefik:
    image: traefik:v3
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

Prometheus integration

Prometheus can discover targets using container labels:

services:
  api:
    image: myapp-api
    labels:
      prometheus.scrape: "true"
      prometheus.port: "9090"
      prometheus.path: "/metrics"

  worker:
    image: myapp-worker
    labels:
      prometheus.scrape: "true"
      prometheus.port: "9091"

Organization patterns

Use a consistent naming convention across your team:

services:
  api:
    image: myapp-api
    labels:
      # Ownership
      com.example.team: "platform"
      com.example.contact: "platform-team@example.com"

      # Environment
      com.example.env: "${ENV:-dev}"
      com.example.version: "${VERSION:-latest}"

      # Operational
      com.example.backup: "true"
      com.example.log-level: "info"

Pro tip

Labels on containers are different from labels on networks and volumes:

services:
  app:
    image: myapp
    labels:
      app.tier: "frontend"     # Container label

networks:
  frontend:
    labels:
      network.purpose: "public-facing"  # Network label

volumes:
  data:
    labels:
      volume.backup: "daily"   # Volume label

Each resource type has its own labels, and you can filter each independently.

Further reading