Docker Compose Project Name: A Complete Guide to Naming and Scoping Your Stacks
Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.
Every Docker Compose stack has an identity that goes beyond the services defined in its YAML file: the docker compose project name. This name controls how containers, networks, and volumes are labeled and grouped, and getting it wrong is one of the most common sources of confusing, duplicate, or orphaned resources on a development machine or a small VPS. This guide explains exactly how the docker compose project name is determined, how to override it, and how to use it deliberately when running multiple environments side by side.
What Is a Docker Compose Project Name?
When you run docker compose up, Compose doesn’t just create containers — it creates a project, a logical namespace that groups every resource belonging to that invocation. The docker compose project name is the string used to prefix container names, form the default network name, and tag every object Compose creates with a com.docker.compose.project label.
If you never set it explicitly, Compose derives the docker compose project name from the name of the directory containing your compose.yaml (or docker-compose.yml) file, lowercased and stripped of characters that aren’t valid in Docker resource names. So a project living in /home/user/my-app/ becomes project name my-app, and a service named web in that file ends up running as a container called my-app-web-1.
This matters more than it looks. Two checkouts of the same repository in differently named folders will get two different project names and therefore two entirely separate sets of containers, networks, and volumes — even though the compose.yaml file is byte-for-byte identical. Conversely, two unrelated projects that happen to share a folder name will collide.
Why the Project Name Matters in Practice
The docker compose project name isn’t cosmetic. It determines:
<project>-<service>-<replica-number><project>_defaultdocker compose down, docker compose stop, and docker compose ps will target, since these commands operate per-project by defaultIf you’ve ever run docker compose up from two different directories that both contain services named db, and been surprised that they don’t conflict on the container name, the docker compose project name is the reason why — they were silently placed in separate namespaces.
How Compose Determines the Docker Compose Project Name
Compose resolves the project name using a defined precedence order, checked top to bottom until one is found:
1. The -p / --project-name CLI Flag
The most explicit method. Passing -p on the command line overrides everything else:
docker compose -p staging-app up -d
Every container, network, and volume created by this invocation will be scoped under staging-app, regardless of what the working directory or compose file is named.
2. The COMPOSE_PROJECT_NAME Environment Variable
If no -p flag is given, Compose checks for COMPOSE_PROJECT_NAME in the environment or in a .env file next to the compose file:
export COMPOSE_PROJECT_NAME=my-service-prod
docker compose up -d
This is the preferred approach for CI/CD pipelines and deploy scripts, since it avoids repeating the flag on every command and keeps naming consistent across up, down, logs, and exec invocations.
3. The name: Top-Level Key in compose.yaml
Recent versions of the Compose Specification support declaring the project name directly inside the file:
name: inventory-service
services:
api:
image: inventory-api:latest
ports:
- "8080:8080"
redis:
image: redis:7-alpine
This is arguably the most maintainable option for a project that’s meant to always run under one identity: it travels with the repository, so anyone who clones it and runs docker compose up gets the same project name without needing to remember an environment variable or a flag.
4. The Directory Name (Default Fallback)
If none of the above are set, Compose falls back to the basename of the directory holding the compose file, normalized to lowercase alphanumerics, hyphens, and underscores.
Overriding the Docker Compose Project Name for Multiple Environments
A common real-world need is running the same compose.yaml multiple times with different data and different container sets — for example, a staging copy and a production copy on the same host, or several client instances of the same application. The docker compose project name is exactly the mechanism designed for this.
# Client A
docker compose -p client-a --env-file .env.client-a up -d
# Client B
docker compose -p client-b --env-file .env.client-b up -d
Because each invocation uses a distinct docker compose project name, both stacks can run concurrently on the same host without port, network, or container-name collisions (assuming the compose file itself doesn’t hardcode a fixed host port for every environment).
Running Isolated Copies for Testing
This same pattern is useful for spinning up a throwaway copy of a stack for integration testing without touching your regular development containers:
docker compose -p test-run-$(date +%s) up -d --build
# run your test suite against the ephemeral stack
docker compose -p test-run-$(date +%s) down -v
Because the project name is unique per run, you avoid any risk of a test accidentally reusing a volume or network from your main development stack.
Checking and Auditing Project Names on a Running Host
Once a host accumulates several stacks over time, it’s easy to lose track of which containers belong to which project. Compose and the Docker CLI both expose this information through labels.
docker ps --format 'table {{.Names}}t{{.Label "com.docker.compose.project"}}'
You can also list every distinct project currently known to Compose:
docker compose ls
This prints each project name Compose is aware of, along with its status (running, exited) and the config files associated with it — useful before running a docker compose down you don’t want to accidentally scope too broadly.
Cleaning Up by Project Name
Because down is scoped to the resolved project name, cleanup is precise as long as you’re consistent about which name you pass:
docker compose -p client-a down -v
This removes only client-a‘s containers, its default network, and (with -v) its anonymous volumes — leaving client-b and anything else on the host untouched. For a deeper walkthrough of down behavior, including what it does and doesn’t remove by default, see this guide to stopping stacks with Docker Compose.
Common Mistakes and Pitfalls
A few docker compose project name mistakes come up repeatedly enough to call out directly:
/opt/my-app to /opt/myapp-v2 on a production host silently changes the docker compose project name on the next up, and Compose will create an entirely new set of containers and networks rather than recognizing the existing ones.-p or COMPOSE_PROJECT_NAME.docker compose down without -p targets “everything.” It only targets the resolved project for the current working directory. Run it from the wrong directory (or after a directory rename) and it may report nothing to remove, leaving old containers running.COMPOSE_PROJECT_NAME in their shell profile and another doesn’t, the same repository can produce two different project names on two machines, breaking assumptions in scripts that reference container names directly.Explicitly declaring the docker compose project name — either in the compose file’s name: key or in a checked-in .env file — removes this entire class of bug, since the name no longer depends on where the file happens to be checked out.
Docker Compose Project Name and Networking
Every project gets its own default bridge network, named <project>_default, unless the compose file defines custom networks. Containers within the same project can reach each other by service name over this network; containers in different projects cannot, by default, since they sit on separate networks entirely.
name: shop-backend
services:
api:
image: shop-api:latest
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
With this file, api can reach the database at hostname db because both containers share the shop-backend_default network. If you need cross-project communication — for instance, a reverse proxy stack that must reach containers in several application stacks — you typically declare a shared external network rather than relying on the default per-project one. If you’re running Postgres this way, the Postgres Docker Compose setup guide covers volume and environment configuration in more depth, and the Docker Compose volumes guide is useful background on how named volumes like db-data above are scoped per project.
Interaction With Environment Variables and Secrets
The docker compose project name doesn’t just affect naming — it can also be referenced inside your compose file and .env file for building dynamic values, such as prefixing a database name or a bucket path per environment. Managing this cleanly usually means keeping environment-specific values out of the compose file itself. The Docker Compose env variables guide and the related environment variables reference both go into detail on .env file precedence, which is worth understanding alongside project naming since both are resolved from the same working directory context. If you’re also handling passwords or API keys, pair project-name discipline with proper secret handling as described in the Docker Compose secrets guide rather than placing them directly in environment variables.
Choosing a Naming Convention
For teams running several stacks on shared infrastructure, a consistent docker compose project name convention avoids ambiguity months later when nobody remembers which container belongs to which client or environment. A reasonable pattern:
name: <service>-<environment>
For example: inventory-staging, inventory-prod, billing-staging, billing-prod. This keeps docker compose ls output readable and makes docker ps output self-explanatory even on a host running a dozen stacks.
If you’re deploying several such stacks to a single VPS, make sure the host has enough headroom in memory and disk before you start stacking projects — an unmanaged VPS hosting guide is a good reference for what to check before committing to a given instance size. For hosts specifically, DigitalOcean, Hetzner, and Vultr are commonly used providers for running small-to-medium Compose deployments.
Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.
Controlling the Project Name to Affect All Container Names at Once
Rather than setting container_name on every service, you can control the prefix applied to all of them by setting the Compose project name. This affects containers, networks, volumes, and default hostnames together, which is often what you actually want.
docker compose -p myapp up -d
Or persist it via an environment variable so you don’t have to remember the flag every time:
export COMPOSE_PROJECT_NAME=myapp
docker compose up -d
You can also set name: at the top level of the compose file itself (supported in the Compose Specification):
name: myapp
services:
api:
image: node:20-alpine
db:
image: postgres:16
This is a good middle ground between the default and the fully-manual container_name approach — it keeps the automatic <project>-<service>-<n> numbering (so scaling still works) while making sure the project prefix is stable no matter where the checkout lives on disk.
Environment Files and .env Interaction
Compose automatically reads a .env file in the project directory, and COMPOSE_PROJECT_NAME can be set there too. This is useful in CI pipelines where the checkout path is unpredictable but you still want deterministic container and network names between runs. If you’re managing secrets and environment variables alongside naming, it’s worth reading through a guide on managing Compose environment variables the right way to keep the two concerns properly separated.
Using Hostnames Alongside Container Names
Container names and network hostnames are related but not identical concepts. By default, Compose creates a network for the project and registers each service’s container name (or the service name itself) as a resolvable hostname on that network. This means other containers on the same Compose network can reach db by that name regardless of what the actual container_name is set to, because Compose’s embedded DNS resolves service names automatically.
services:
api:
image: node:20-alpine
container_name: myapp-api
environment:
DATABASE_URL: postgres://user:pass@db:5432/appdb
db:
image: postgres:16
container_name: myapp-db
Notice that DATABASE_URL references db, the service name, not myapp-db, the container name. This distinction trips people up constantly: service names are what other containers use for internal DNS resolution, while container_name is what you see in docker ps, docker logs, and host-level tooling. You can also override the internal hostname explicitly with hostname: if you need it to differ from the service name for some reason.
Debugging Name Conflicts
If you try to start a stack and get an error like “container name already in use,” it’s almost always because a previous container with that fixed container_name is still around — stopped but not removed. Docker won’t let two containers share a name even if one is stopped. Cleaning this up is usually a matter of running docker compose down before docker compose up again, but if you’re troubleshooting a stuck container manually:
docker rm -f myapp-api
docker compose up -d api
For a systematic breakdown of what docker compose down actually removes (and what it leaves behind, like named volumes), see this full guide to stopping stacks. If containers seem to be starting but immediately failing, checking logs is the next step — this debugging guide to Compose logs covers the common patterns for tracing failures back to their cause.
Renaming Existing Containers Without Recreating Them
Sometimes you inherit a stack where containers were never given explicit names, and you want to fix that without tearing everything down. Docker does support renaming a running container directly with docker rename, independent of Compose:
docker rename myapp-api-1 myapp-api
This works, but it’s a trap if you don’t also update your compose file: the next time you run docker compose up, Compose compares the current container against what the compose file describes, notices the container name doesn’t match what it expects (myapp-api-1), and will typically create a brand-new container rather than adopting the renamed one. If you want a name to stick permanently, always add container_name to the compose file rather than relying on a one-off docker rename call.
Recreating Containers Safely After a Naming Change
If you do update the compose file to add or change a container_name, the cleanest way to apply it is:
docker compose up -d --force-recreate
This tells Compose to recreate containers even if it thinks nothing else changed, which forces the new name to take effect. For stateful services like databases, make sure your data lives in a named volume (not an anonymous one) before doing this, so the recreate doesn’t lose data — the Postgres Compose setup guide and the Redis Compose setup guide both cover volume configuration in detail if you’re naming containers for stateful services specifically.
Naming Considerations in Multi-Environment Setups
Teams that run the same compose file across development, staging, and production often hit naming collisions when multiple environments run on the same Docker host — for instance, a shared CI runner. A few approaches handle this cleanly:
COMPOSE_PROJECT_NAME (or the -p flag) set per environment, e.g. myapp-staging vs. myapp-prod, and avoid hardcoded container_name values entirely so the project prefix does the differentiating work.container_name values (for external monitoring integration, say), interpolate the environment into the name using a variable: container_name: myapp-api-${ENVIRONMENT:-dev}.compose.override.yaml file rather than duplicating the whole base file, which also makes diffs between environments easier to review.If your naming strategy also needs to account for image rebuilds after a Dockerfile change, it’s worth pairing this with a look at how docker compose rebuild and docker compose up --build interact with existing containers, covered in the Compose rebuild guide. And if you’re still deciding between a single Dockerfile and a full Compose setup for a small project, the Dockerfile vs Docker Compose comparison is a useful starting point before naming even becomes a concern.
Hosting Considerations for Multi-Container Stacks
Naming discipline matters more as a stack grows, and stack size is often driven by where you’re hosting it. A small VPS with limited memory encourages fewer, well-named services; a larger box lets you run full staging and production stacks side by side, which is exactly the scenario where consistent project and container naming saves the most debugging time. If you’re evaluating VPS providers for running a multi-container Compose stack, DigitalOcean and Vultr both offer straightforward Docker-ready images that make it easy to standardize your naming conventions from the first deploy.
Container Naming in CI/CD and Automated Deployments
In automated pipelines, a stable docker compose container name is often more useful than in manual development, because scripts and health checks need something predictable to target. A typical pattern in a deploy script:
#!/usr/bin/env bash
set -euo pipefail
docker compose -f docker-compose.prod.yml up -d
docker exec app-backend curl -f http://localhost:3000/health || exit 1
Here, app-backend is a fixed name set in the compose file, and the deploy script references it directly rather than parsing docker compose ps output to discover the generated name. This is a small thing, but it removes a class of fragile string-parsing from deploy scripts. If you’re running this kind of pipeline on a self-managed server rather than a managed platform, a guide on self-hosting n8n with Docker shows a comparable pattern of naming containers explicitly for automation reliability in a real production stack.
Combining Fixed Names with Health Checks
Compose’s healthcheck directive works independently of container_name, but the two are often used together in production, since a fixed name makes it trivial to query a specific container’s health status externally:
docker inspect --format='{{.State.Health.Status}}' app-backend
This pattern is common in monitoring scripts and works cleanly as long as the container name doesn’t change between deployments — another reason to standardize on explicit names for critical, single-instance services early rather than retrofitting them later.
FAQ
Does changing the docker compose project name destroy my existing containers?
No, but it does orphan them. Compose won’t automatically stop or remove containers from the old project name — they’ll keep running under their original name while a new up under the new project name creates a fresh, separate set of containers. You’d need to manually stop and remove the old ones, or reference them with the old -p value to bring them down cleanly.
Can two Compose projects share the same volume?
Yes, but only if you declare the volume as external: true and reference its real name rather than letting Compose scope it under the project. Otherwise each project gets its own volume, even if the volume key in the YAML is spelled identically.
Is the docker compose project name case-sensitive?
Compose normalizes project names to lowercase, along with converting other unsupported characters. If you set COMPOSE_PROJECT_NAME=MyApp, Compose will still resolve resource names using a lowercased form internally, so it’s best to just write project names in lowercase from the start to avoid confusion.
What happens if I don’t set a docker compose project name and run the same compose file from two different clones of the same repo?
Each clone’s directory name becomes the project name by default. If both clones share the same directory name (for example, both cloned as myapp), you’ll get a naming collision the moment both try to run concurrently on the same host. If the directory names differ, you’ll instead get two silently separate stacks, which is usually not what you want either — this is precisely the scenario where declaring name: explicitly in the compose file avoids surprises.
Conclusion
The docker compose project name is a small piece of configuration with outsized consequences: it decides how your containers are named, how they’re networked, and which ones a given docker compose down or docker compose stop will actually touch. Relying on the directory-name default works fine for a single, casually-run project, but anything running in CI, on a shared host, or in multiple environments benefits from setting the docker compose project name explicitly — via the name: key in the compose file, COMPOSE_PROJECT_NAME, or the -p flag. Doing so removes an entire category of “why are there two databases running” debugging sessions, and makes cleanup and auditing predictable as the number of stacks on a host grows. For the full command reference on project scoping and other flags, the official Docker Compose CLI reference and the Compose Specification documentation are the authoritative sources to check against your installed version.
Leave a Reply