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

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.
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:
- Finding which container belongs to which project or environment becomes a scavenger hunt
- Filtering logs or metrics by service or owner is not easy
- Spinning up subsets of containers for testing or blue-green deployments takes manual editing of Compose files
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:
com.myorg.project
com.myorg.environment
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:
- Project-level directories: Each microservice project lives in its own Git repo with a dedicated Compose file (and overrides)
- Central โorchestrationโ repo: A master Compose setup imports via Composeโs support for external files so I can spin up entire suites together
- 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
- Profiles for tiers:
core
,monitoring
,batch
,experimental
, you get to name it. - 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.