Docker container names are unique across the whole daemon, and a stopped container keeps its name. That is the entire cause. docker ps shows only running containers, so the name looks free while something you forgot about is still holding it.
docker ps -a --filter "name=^/postgres$" \
--format 'table {{.ID}}\t{{.Status}}\t{{.Image}}'
ID STATUS IMAGE
3f2c1a9e4b7d Exited (137) 2 hours ago postgres:16
The ^/name$ anchoring matters. Docker's name filter is a substring match by default, so --filter name=app also matches myapp-worker. The leading slash is part of how Docker stores the name internally.
Decide before you delete
The reflex is docker rm -f. Stop for a second, because a stopped container is not empty.
docker inspect postgres --format '{{range .Mounts}}{{.Type}} {{.Name}}{{printf "\n"}}{{end}}'
volume a1b2c3d4e5f6...
A long hex name is an anonymous volume: created automatically because the image declared a VOLUME and you did not supply one. docker rm postgres leaves it behind as an orphan, and docker rm -v postgres deletes it along with whatever was in it. Neither is what you want if that was your only copy of the database.
A readable name is a named volume and survives container removal in both cases.
Also consider that the old container's writable layer holds anything written outside a volume. If you exec'd in and edited a config, that is in the layer and goes away with the container.
If the data matters, start the existing container instead:
docker start postgres
docker logs --tail 20 postgres
Why it exited
Exited (137) above is worth reading rather than skipping. The exit code tells you whether this was deliberate:
| Code | Meaning |
|---|---|
0 | Clean exit |
1 | Application error |
125 | The docker run command itself was invalid |
126 | Command found but not executable |
127 | Command not found |
137 | SIGKILL, usually the OOM killer or a docker stop timeout |
143 | SIGTERM, a normal docker stop |
137 specifically often means the container was killed for exceeding its memory limit:
docker inspect postgres --format '{{.State.OOMKilled}}'
If that prints true, freeing the name is not your problem. The container will exit the same way again.
Fix: Remove it
Once you are sure:
docker rm -f postgres # stops if running, then removes
docker rm -f -v postgres # also removes anonymous volumes
Or rename the old one to keep it around:
docker rename postgres postgres-old
docker run --name postgres ...
Stop it happening again
Use --rm for anything disposable. The container is removed as soon as it exits, so the name is never held:
docker run --rm --name migrate myapp:1.0 ./migrate
Note that --rm and -d together are fine, but --rm means you cannot read the logs after it exits, which is the main reason not to use it for something you are debugging.
Do not name containers in CI at all. A fixed name guarantees a collision the moment two jobs run on the same runner. Let Docker generate one, or include something unique:
docker run --rm --name "build-${CI_JOB_ID}" myapp:1.0 make test
Make cleanup idempotent in scripts that must use a fixed name:
docker rm -f postgres 2>/dev/null || true
docker run -d --name postgres postgres:16
The || true stops the script failing on the first run, when there is nothing to remove.
With Docker Compose
Compose usually manages this for you, deriving names from the project and service. You hit the conflict when:
A service sets container_name:. That opts out of Compose's naming, so it collides with anything else using that name, and it prevents scaling the service beyond one replica. Remove it unless you genuinely need a fixed name:
services:
db:
image: postgres:16
container_name: postgres # remove this
A service was renamed or removed. Its container is now an orphan and Compose keeps it around unless told otherwise:
docker compose up -d --remove-orphans
The project name changed. Compose derives the project from the directory name by default, so cloning the repo to a different folder creates a second set of containers. Pin it:
docker compose -p myapp up -d
Clearing up broadly
docker container prune # all stopped containers
docker container prune --filter "until=24h"
docker system prune -a also removes every image not used by a running container, which on a development machine means a long rebuild afterwards. It is rarely what you want when you were only trying to free a name.
A checklist
docker ps -a --filter "name=^/<name>$". The-ais the point.- Read the STATUS column.
Exited (137)means it was killed, probably OOM. docker inspect <name> --format '{{range .Mounts}}...'before deleting. Anonymous volumes hold data.- Data matters →
docker start <name>, ordocker renameit out of the way. - Otherwise
docker rm -f <name>, adding-vonly if you want the anonymous volumes gone too. - Use
--rmfor disposable containers so the name is released automatically. - In CI, do not use fixed names, or suffix them with the job ID.
- Compose → drop
container_name:, and use--remove-orphans.
Frequently Asked Questions
Why does Docker say the name is in use when nothing is running?
Because a stopped container still owns its name, and docker ps only lists running containers. The name stays reserved for the entire lifetime of the container object, not just while it is running, and that object persists until you remove it. docker ps -a shows it. This is by design: a stopped container retains its writable layer, its configuration and its logs, so it remains a distinct thing that has to keep a distinct name.
Is it safe to run docker rm -f on the conflicting container?
Usually, but check first. Removing a container deletes its writable layer, so anything written outside a volume is gone, and docker rm -v also deletes anonymous volumes. Anonymous volumes are created automatically when an image declares VOLUME and you did not supply one, which is exactly how a lot of local database containers end up storing real data in a volume nobody named. Run docker inspect <name> --format '{{range .Mounts}}{{.Type}} {{.Name}}{{end}}' first: a long hex name is anonymous and worth thinking about.
How do I stop this happening in CI?
Do not give containers fixed names on a shared runner. Two concurrent jobs using --name postgres will collide, and the failure looks random because it depends on timing. Either let Docker generate the name, or make it unique with something like --name "db-${CI_JOB_ID}". Add --rm so the container and its name disappear as soon as it exits. Where a fixed name is genuinely required, precede the run with docker rm -f <name> 2>/dev/null || true so the step is idempotent.
Why does Docker Compose hit this error?
Three common reasons. A service with an explicit container_name: opts out of Compose's automatic naming, so it can collide with anything else and cannot be scaled past one replica. A service that was renamed or deleted leaves an orphan container behind, which docker compose up --remove-orphans clears. And the project name defaults to the directory name, so the same repository cloned into a second folder creates a parallel set of containers; pin it with -p or COMPOSE_PROJECT_NAME to avoid that.
What does exit code 137 mean on the old container?
The process received SIGKILL. In containers that is most often the kernel OOM killer terminating it for exceeding its memory limit, and sometimes docker stop running out of patience after its grace period and escalating from SIGTERM. Check with docker inspect <name> --format '{{.State.OOMKilled}}'. If that is true, raising the memory limit or fixing the leak matters more than freeing the name, because a fresh container will exit exactly the same way.