DEV Community

Cover image for Solved: How to add dynamic names in a docker compose file?
Darian Vance
Darian Vance

Posted on • Originally published at wp.me

Solved: How to add dynamic names in a docker compose file?

šŸš€ Executive Summary

TL;DR: Hardcoded container names in Docker Compose lead to collisions and lost productivity in shared development environments because Docker Compose is declarative and performs only one-time variable substitution. Solutions involve using environment variables via a .env file for static differentiation, wrapper shell scripts for true runtime dynamism (e.g., based on Git branch), or templating engines for complex, conditional configurations.

šŸŽÆ Key Takeaways

  • Docker Compose is declarative, performing a one-time variable substitution, and lacks inherent dynamic logic for container\_name without external scripting.
  • Environment variables, particularly loaded from a .env file, offer a simple, Docker-native way to provide static but differentiated container names for local development.
  • Wrapper shell scripts enable truly dynamic container naming by calculating and exporting environment variables (e.g., current Git branch) before executing the docker-compose up command.
  • For complex scenarios requiring conditional logic or extensive configuration, templating engines like Gomplate or Jinja2 can pre-process a template into a final docker-compose.yml file.

Learn why Docker Compose struggles with dynamic container names and discover three practical solutions, from simple environment variables to powerful shell scripts, to manage your services effectively.

How to Name Your Containers Dynamically in Docker Compose (Without Losing Your Mind)

I still remember the day. It was about 3 PM on a Tuesday. Two of our best junior engineers were practically at each other’s throats. One minute, Jane’s feature was working perfectly on our shared dev-integration-01 server. The next, it was completely broken, throwing weird database connection errors. Meanwhile, Mike, working on a different feature, was complaining that his changes weren’t showing up at all. It took us a solid hour to figure out what happened: they both ran docker-compose up from the same project directory for their respective feature branches. Since the container\_name was hardcoded, Mike’s deployment simply killed and replaced Jane’s containers. An hour of productivity, gone. All because of a static name in a YAML file. That’s when I made it a team rule: we never hardcode container names for non-production environments again.

First, Why Is This So Annoying?

Before we dive into the fixes, let’s understand the root of the problem. A docker-compose.yml file is declarative, not procedural. It’s a blueprint that says, ā€œthis is the state I want the world to be in.ā€ It’s not a script that runs commands. When you run docker-compose up, it does a simple, one-time variable substitution from your environment and then builds its plan. It has no concept of loops, functions, or dynamic logic like ā€œget the current git branch nameā€ on its own. It’s a feature, not a bug, designed for simplicity and predictability. But that simplicity is exactly what causes this headache.

The Solutions: From Quick Hack to Enterprise-Grade

I’ve seen this problem tackled in a few ways over the years. Here are my three go-to methods, ranging from the simplest to the most robust.

1. The Quick Fix: Environment Variables and a .env File

This is the most common and ā€œDocker-nativeā€ way to handle this. Docker Compose will automatically look for a file named .env in the same directory and load its contents as environment variables. This is perfect for differentiating between a user’s local machine or different static environments.

Step 1: Modify your docker-compose.yml

Instead of a hardcoded name, use variable substitution syntax ${VARIABLE_NAME}. You can even provide a default fallback value with ${VARIABLE:-default_value}.

version: '3.8'
services:
  web:
    image: nginx:latest
    container_name: ${PROJECT_NAME:-myproject}-web
    ports:
      - "8080:80"
  db:
    image: postgres:14
    container_name: ${PROJECT_NAME:-myproject}-db
    environment:
      - POSTGRES_PASSWORD=supersecret
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a .env file

In the same directory, create a file named .env. This file should not be committed to source control (add it to your .gitignore!).

# .env file for Darian's local setup
PROJECT_NAME=darian-feature-x
Enter fullscreen mode Exit fullscreen mode

Now, when I run docker-compose up, my containers will be named darian-feature-x-web and darian-feature-x-db. If Jane creates her own .env file with PROJECT\_NAME=jane-bugfix-y, her containers won’t collide with mine. Simple and effective.

Pro Tip: Create a .env.example file and commit that to your repository. It shows other developers what variables they need to create in their own local .env file to get the project running.

2. The ā€œIn the Trenchesā€ Fix: A Wrapper Shell Script

The .env file is great, but it’s static. What if you want the name to be truly dynamic, based on something like the current Git branch, the username, or a timestamp? That’s where a simple shell script comes in. This is my personal favorite for complex development environments.

The idea is to use a script to set the environment variables just before running the Docker Compose command.

Step 1: Keep your docker-compose.yml the same as above.

Make sure it’s expecting an environment variable, like ${PROJECT_NAME}.

Step 2: Create a wrapper script (e.g., start-dev.sh)

This script will calculate the dynamic name and then execute the compose command.

#!/bin/bash

# Clean up branch name to be Docker-friendly (remove slashes, etc.)
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | sed 's/[^a-zA-Z0-9]/-/g')

# Export the variable so docker-compose can see it
export PROJECT_NAME="myapp-${BRANCH_NAME}"

echo "Starting environment for project: ${PROJECT_NAME}"

# Pass all other arguments to docker-compose
docker-compose up "$@"
Enter fullscreen mode Exit fullscreen mode

Now, instead of running docker-compose up, the team runs bash start-dev.sh. If I’m on the feature/user-auth branch, my containers will be named myapp-feature-user-auth-web, etc. This completely solved the ā€œstomping on each other’s workā€ problem on our shared dev servers.

3. The ā€œWe’ve Outgrown Composeā€ Option: Templating Engines

Sometimes, even a shell script isn’t enough. You might have complex logic, conditional service inclusion, or just a deep-seated hatred for bash scripting. When you reach this point, you’re essentially outgrowing what Docker Compose was designed for. The next logical step is a templating engine.

Tools like gomplate or even just using Python with Jinja2 can pre-process a template file into a final docker-compose.yml.

How it works:

  1. You create a template file, let’s call it docker-compose.yml.tpl, with its own logic.
  2. You run the templating tool, feeding it data (like a values.yaml file).
  3. The tool generates a standard, static docker-compose.yml file.
  4. You then run docker-compose -f generated-compose.yml up.

This approach gives you maximum power and is essentially a ā€œliteā€ version of what tools like Helm do for Kubernetes. It’s overkill for most local development, but for managing multiple complex, semi-permanent environments, it’s a lifesaver.

Warning: This adds a layer of abstraction. If you go this route, make sure the process is well-documented. A new developer should be able to understand that the docker-compose.yml is an ephemeral, generated file and not the source of truth.

Which One Should You Choose?

As with most things in DevOps, the answer is ā€œit depends.ā€ Here’s my simple breakdown:

Solution Best For Pros Cons
.env File Individual local development, simple environment splits (dev/staging). Extremely simple, built-in functionality. Static; requires manual changes for different contexts.
Wrapper Script Shared dev servers, CI/CD, pull request environments. Truly dynamic, flexible, automates naming conventions. Adds another file/script to maintain.
Templating Engine Complex deployments with conditional logic or many configurable parts. Maximum power and logic, prepares you for Kubernetes-style thinking. Overkill for most projects, adds a build step and complexity.

My advice? Start with the .env file. When you find yourself wishing it could do more, graduate to the wrapper script. It’s the sweet spot for 90% of the use cases I’ve encountered. And if you’re managing dozens of microservices with complex interdependencies… well, it might be time to start learning Helm.

Happy containerizing,

Darian Vance

Senior DevOps Engineer & Lead Cloud Architect, TechResolve


Darian Vance

šŸ‘‰ Read the original article on TechResolve.blog


ā˜• Support my work

If this article helped you, you can buy me a coffee:

šŸ‘‰ https://buymeacoffee.com/darianvance

Top comments (0)