Home ยป Containers ยป How I Use Docker Labels and Compose Tricks to Organize 75+ Containers
Containers

How I Use Docker Labels and Compose Tricks to Organize 75+ Containers

Streamline your container management with Docker labels and Compose tricks for efficient organization and control.

When you start to have a large number of containers in your fleet, these can begin to become hard to manage. If you are juggling lots of services across many different projects and environments, this is especially true. I have started to rely heavily on Docker labels and other tricks in Docker Compose. With the labels and other strategies, you can categorize, filter, and manage more than fifty containers in a single environment. All in addition to keeping things declarative and version-controlled.

What are Docker labels?

Docker labels are key value pairs that attach to Docker objects like containers. However, you can also attach these to images, networks, and volumes. When you do, it attaches metadata directly to these objects. Also labels can be defined in your Dockerfiles or Docker Compose service definitions.

For example, you can specify project name, environment, version, or owner. This makes it much easier to filter, organize, and automate tasks across large container fleets. Labels provide a flexible and standardized way to improve Docker resources information without changing the runtime behavior.

Docker command syntax
Docker command syntax

Examples

1. In a Dockerfile

FROM nginx:alpine
LABEL com.myorg.project="payment-api" \
      com.myorg.environment="production" \
      com.myorg.version="1.2.3"

2. In a docker-compose.yml

version: "3.9"
services:
  web:
    image: myorg/web:latest
    labels:
      com.myorg.project: payment-api
      com.myorg.environment: production
      com.myorg.service-type: frontend

Docker labels and other tricks

When youโ€™re running just a handful of containers, you can probably keep track of everything manually. But once you scale up and add tons of monitoring agents, sidecars, application services, databases, proxies, and CI/CD runners, the number of running containers you are keeping up with can explode.

Without a system for intuitive labeling of your containers:

Docker labels let you attach metadata to containers, images, networks, and volumes. Docker Compose supports labels on services out of the box. By using a good labeling scheme and combining it with Composeโ€™s advanced features (override files, profiles, extensions, variable interpolation), you get:

  • Easy metadata-based filtering in CLI commands, dashboards, monitoring systems
  • Declarative grouping of containers
  • Simplified spawning of related services with Compose profiles

As a side note, I have a cool open-source Docker dashboard solution I built to provide a lightning fast way to have a view of your Docker containers in the environment. You can visit the Github repo here:

Also, my YouTube vid here:

Below is the step-by-step approach to using Docker labels to help tame a sprawling Docker container environment.

Defining a Labeling Convention

A good naming convention is the foundation. I use three core labels for every service:

  1. com.myorg.project
  2. com.myorg.environment
  3. com.myorg.team

Optionally, I add:

  • com.myorg.service-type (e.g. frontend, backend, database, cache)
  • com.myorg.version (semantic version or Git SHA)
  • com.myorg.owner (person or team)

By prefixing with my organizationโ€™s reverse-domain (com.myorg), I avoid collisions with system or third-party labels and keep everything namespaced.

Hereโ€™s an example snippet in a docker-compose.yml:

services:
  web:
    image: myorg/web:2.3.1
    labels:
      com.myorg.project: payment-api
      com.myorg.environment: production
      com.myorg.team: billing
      com.myorg.service-type: backend
      com.myorg.version: 2.3.1
  redis:
    image: redis:6.2
    labels:
      com.myorg.project: payment-api
      com.myorg.environment: production
      com.myorg.team: billing
      com.myorg.service-type: cache
      com.myorg.version: "6.2"

With this in place, you can run commands like:

docker ps --filter "label=com.myorg.project=payment-api"
docker inspect --format='{{json .Config.Labels}}' $(docker ps -q)

and immediately narrow down to relevant containers.

Grouping Services with Compose Profiles

Docker Compose v3 introduced profiles. These are logical groups of services that you can enable or disable at runtime. I use profiles to split services into โ€œcoreโ€ vs. โ€œoptionalโ€ groups, or to segment by environment.

In the same Compose file:

services:
  web:
    profiles: ["core"]
    # โ€ฆexisting configโ€ฆ
  redis:
    profiles: ["core"]
  metrics:
    image: prom/prometheus
    profiles: ["monitoring"]
    labels:
      com.myorg.project: payment-api
      com.myorg.environment: production
      com.myorg.team: devops
      com.myorg.service-type: monitoring

Then to bring up just your core API:

docker compose --profile core up -d

Or include monitoring:

docker compose --profile core --profile monitoring up -d

This eliminates the need for multiple Compose files just to kick off subsets of your environment.

Using override files for environment-specific settings

I keep a base docker-compose.yml for common configuration, then use override files like docker-compose.prod.yml and docker-compose.dev.yml for environment-specific tweaks:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Inside docker-compose.prod.yml, I might add labels indicating the production tier or version:

services:
  web:
    labels:
      com.myorg.environment: production
      com.myorg.version: "${VERSION}"
    environment:
      - LOG_LEVEL=info
  redis:
    labels:
      com.myorg.environment: production

Meanwhile docker-compose.dev.yml would set environment: development and mount source code directories for live reload.

Using variable interpolation and extensions

To avoid repetition, I use YAML anchors, aliases, and environment variable interpolation. For instance, define a common labels block:

x-common-labels: &common-labels
  com.myorg.project: payment-api
  com.myorg.team: billing

services:
  web:
    image: myorg/web:${VERSION:-latest}
    labels:
      <<: *common-labels
      com.myorg.environment: ${ENV:-development}
      com.myorg.service-type: backend
  redis:
    image: redis:${REDIS_VERSION:-6.2}
    labels:
      <<: *common-labels
      com.myorg.environment: ${ENV:-development}
      com.myorg.service-type: cache

With this you only update the anchor once and every service picks it up. Environment variables set in a .env file or CI/CD pipeline automatically propagate into the Compose run.

Filtering and visualizing with Docker labels

Once containers are labeled, toolchains that integrate with Docker can leverage those labels. For example:

  • Monitoring/Alerting: Prometheusโ€™ Docker SD can filter targets by label.
  • Logging: Use the Docker logging driver to include labels in log metadata, then filter logs in Elasticsearch or Grafana.
  • Dashboards: Portainer and other UIs let you group containers by label values, making navigation intuitive.

Example Prometheus service discovery:

- job_name: 'docker'
  docker_sd_configs:
    - host: unix:///var/run/docker.sock
  relabel_configs:
    - source_labels: ['__meta_docker_container_label_com_myorg_project']
      regex: payment-api
      action: keep

This scraps only containers belonging to your payment-api project, reducing noise.

Handling 75+ containers in a single environment

By combining the ways above, I can manage an environment with over 75+ containers. These span multiple microservices, monitoring stacks, message brokers, and utilities. In addition to labels, I have the following configured:

  1. Project-level directories: Each microservice project lives in its own Git repo with a dedicated Compose file (and overrides)
  2. Central โ€œorchestrationโ€ repo: A master Compose setup imports via Composeโ€™s support for external files so I can spin up entire suites together
  3. Label-first mentality: Every new service must include the label anchor before merge. This is enforced via a pre-commit Git hook that checks for required labels
  4. Profiles for tiers: core, monitoring, batch, experimental, you get to name it.
  5. Consistent versioning: I inject the current Git SHA or semantic version into com.myorg.version. This helps to make it clear what is running where

With labels like this in place, you can:

  • Run a health check across containers in โ€œpayment-apiโ€ and โ€œmonitoringโ€ only
  • Update a single microservice without touching the rest, by filtering on labels and using docker compose up service-name
  • Identify orphaned or old containers by querying labels like com.myorg.version!=${CURRENT}, then cleaning them up

Automation and governance

To make sure of consistency:

  • I include a linting step in CI that validates Compose files against a JSON schema enforcing the presence of x-common-labels, required labels on services, and valid profile names.
  • A nightly job runs docker ps --filter label=com.myorg.environment=production --format "{{.Names}}: {{.Label \"com.myorg.version\"}}" and compares versions against Git tags to flag drift
  • Version rollouts become a matter of updating the anchor or override file, then running docker compose up -d at the root level

Tips for keeping your labeling strategy simple and useable

There are several tips I recommend for keeping your labeling strategy simple and make it where it is actually something that is useful to use:

  • Keep your label schemes shallow and consistent. Too many dimensions can become confusing
  • Document your labels in a single markdown file in the root of your orchestration repo
  • Use YAML anchors for shared label sets and enforce them via pre-commit
  • Prefix with your organization domain to avoid collisions (e.g. com.myorg.*)
  • Update labels as part of your release process and automate stamping of com.myorg.version from CI

Wrapping up

With Docker labels and Compose tricks, you can have a very flexible and powerful way to control and manage even very large container environments. With the ability to embed the metadata into the service definitions and Compose profiles, you can spin up targeted subsets of containers, filter, and perform effective housekeeping.

One you have a labeling convention, be sure to bake it into your development lifecycle. You can do this with different tools and processes like commit hooks, CI validation, and definitely keep things documented. Let me know if Docker labels are something you are using or if you have other tools and techniques for effective management.

Brandon Lee

Brandon Lee is the Senior Writer, Engineer and owner at Virtualizationhowto.com, and a 7-time VMware vExpert, with over two decades of experience in Information Technology. Having worked for numerous Fortune 500 companies as well as in various industries, He has extensive experience in various IT segments and is a strong advocate for open source technologies. Brandon holds many industry certifications, loves the outdoors and spending time with family. Also, he goes through the effort of testing and troubleshooting issues, so you don't have to.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.