What I Was Trying to Do
I had a project called myapp with three services in compose.yaml: web, worker, and redis. We decided to move worker to a separate host, so I removed its definition from compose.yaml and ran docker compose up -d to bring up the remaining services.
docker compose up -d
[+] Running 3/3
✔ Container myapp-redis-1 Started
✔ Container myapp-web-1 Started
WARN[0000] Found orphan containers ([myapp-worker-1]) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
The command didn’t fail — web and redis started fine — but I got a warning about the service I had just deleted. Checking further, the worker container was still running.
docker ps --filter "label=com.docker.compose.project=myapp" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
NAMES IMAGE STATUS
myapp-web-1 myapp-web:latest Up 5 seconds
myapp-redis-1 redis:7 Up 5 seconds
myapp-worker-1 myapp-worker:old Up 3 days
Removing the service definition from compose.yaml apparently didn’t stop the existing container by itself.
Environment
- OS: Ubuntu 22.04.4 LTS
- Docker Engine: 26.1.3
- Docker Compose: v2.27.0 (the
docker composeplugin, not the legacy standalonedocker-composebinary) - Project name:
myapp, derived automatically from the directory name (COMPOSE_PROJECT_NAMEnot set) - Removed service:
worker(deleted fromcompose.yaml, being moved to a separate host)
What I Tried
My first assumption was that docker compose down would clean up every container tied to the project.
docker compose down
[+] Running 2/2
✔ Container myapp-web-1 Removed
✔ Container myapp-redis-1 Removed
web and redis were removed, but nothing happened to worker. Checking docker ps again, myapp-worker-1 was still running.
docker ps --filter "label=com.docker.compose.project=myapp"
CONTAINER ID IMAGE COMMAND STATUS
7f1a2b3c4d5e myapp-worker:old "node worker.js" Up 3 days
down only acts on the services currently listed in compose.yaml — worker, having been removed from the file, was outside its scope. I confirmed I could remove it manually with docker rm -f, but that meant repeating manual cleanup every time a service was retired. I wanted a proper fix instead.
Why This Happens
Docker Compose tags every container it creates with com.docker.compose.project (the project name) and com.docker.compose.service (the service name). When you run docker compose up or docker compose down, Compose compares the service names currently defined in compose.yaml against the containers that actually carry that project’s label.
Any container that carries the project label but whose service name is no longer in compose.yaml — worker, in this case — gets flagged as an “orphan container.” Compose warns about it but does not remove it by default, for both up and down. This is a deliberate safety default: it prevents Compose from silently deleting a running container (and any data attached to it) just because its entry was removed from the file, intentionally or by mistake. Nothing was broken here — it was Compose behaving as designed.
Solution
1. List every container tied to the project
docker ps -a --filter "label=com.docker.compose.project=myapp" --format "table {{.Names}}\t{{.Label \"com.docker.compose.service\"}}\t{{.Status}}"
Check for any service name that no longer appears in compose.yaml.
2. Check whether the orphan container has a named volume attached
Before removing anything, confirm whether it’s using a named volume. Volumes aren’t deleted along with the container, but it’s worth checking both directions before you decide.
docker inspect myapp-worker-1 --format '{{ range .Mounts }}{{ .Name }} {{ end }}'
3. Re-run with --remove-orphans
Once you’ve confirmed it’s safe to remove, add --remove-orphans.
docker compose up -d --remove-orphans
[+] Running 2/2
✔ Container myapp-redis-1 Started
✔ Container myapp-web-1 Started
[+] Removing orphan containers
✔ Container myapp-worker-1 Removed
The same flag works with down.
docker compose down --remove-orphans
4. Remove any now-unneeded named volumes separately (if applicable)
Volumes survive orphan container removal, so clean up anything you’ve confirmed is no longer needed.
docker volume ls --filter "label=com.docker.compose.project=myapp"
docker volume rm myapp_worker-data
Verify It Works
docker compose up -d --remove-orphans
docker ps --filter "label=com.docker.compose.project=myapp"
NAMES IMAGE STATUS
myapp-web-1 myapp-web:latest Up 10 seconds
myapp-redis-1 redis:7 Up 10 seconds
The WARN line is gone, and docker ps no longer lists anything related to worker.
Gotchas
- I assumed
docker compose downcleaned up the whole project, but it only touches services currently defined incompose.yaml. Anything removed from the file is out of its scope. --remove-orphansdeletes containers immediately, with no confirmation prompt. If you split your setup across multiplecompose.yamlfiles with-fbut share the same project name, containers from a file you didn’t mean to touch can get flagged as “orphans” too — so checkdocker psbefore running it, not after.- Named volumes survive even after the container using them is removed. That’s reassuring — you won’t lose data by accident — but it also means leftover volumes quietly eat disk space unless you clean them up with
docker volume rmonce you’re sure they’re no longer needed.
FAQ
Q: Is it safe to always run with --remove-orphans?
Generally yes for a single compose.yaml under one project name. But if you combine multiple files with -f under the same project name, it can remove containers from a file you didn’t intend to touch — check docker ps first.
Q: Why doesn’t Docker Compose remove orphan containers automatically? It’s a safety default. If a service definition were removed from the file by mistake, auto-removing its container could destroy a running workload or data without warning.
Q: What happens to a named volume the orphan container was using?
It isn’t deleted automatically. Once you’ve confirmed it’s no longer needed, remove it separately with docker volume rm or docker volume prune.