Most changes to a container need a new container: a new image tag, a new port, a new environment variable, a restart policy. Data survives the swap only if it lives outside the writable layer.
before after
cache (redis:7) cache (redis:7.4)
│ /data │ /data
▼ ▼
volume cache-data ─────────── volume cache-data (untouched)Find where the data lives
docker inspect --format '{{range .Mounts}}{{.Type}} {{.Name}} -> {{.Destination}}{{println}}{{end}}' cachevolume cache-data -> /dataWhat .Mounts shows | Data is | Before replacing |
|---|---|---|
| A named volume | Safe | Nothing to do |
| An anonymous volume (hash name) | On disk, but tied to a name nobody knows | Copy it into a named volume |
| Nothing at the data path | In the writable layer | docker cp it out first |
Replace on the same named volume
docker stop cache
docker rm cache
docker run -d --name cache -v cache-data:/data redis:7.4
docker exec cache redis-cli GET plan"pro-annual"Stop before you remove. docker stop sends SIGTERM, and databases flush to disk on a clean shutdown. docker rm -f kills the process instead.
Images that create anonymous volumes
Images such as redis and postgres declare a VOLUME for their data directory:
docker image inspect --format '{{json .Config.Volumes}}' redis:7{"/data":{}}Run one without -v and Docker creates an anonymous volume there:
$ docker inspect --format '{{range .Mounts}}{{.Name}} -> {{.Destination}}{{end}}' sessions
372928c5fab5ffbda90b0e7dce8e77eaa007db521caa2827f14b88271e61d2e5 -> /dataA new docker run gets another empty anonymous volume, not this one. The data is still on disk, but the new container does not see it.
Move an anonymous volume into a named one
Run a short-lived helper container that mounts both volumes and copies:
docker stop sessions
OLD=$(docker inspect --format '{{range .Mounts}}{{.Name}}{{end}}' sessions)
docker volume create sessions-data
docker run --rm -v $OLD:/from -v sessions-data:/to alpine:3.20 cp -rp /from/. /to/
docker rm sessions
docker run -d --name sessions -v sessions-data:/data redis:7.4 stopped "sessions" helper (alpine, --rm)
│ /from /to
▼ ▲ │
volume 372928c5… ─────────────┘ ▼
volume sessions-data ◄── new "sessions"cp -rp /from/. /to/ copies the contents of /from, including hidden files, and keeps permissions.
Clean up
The old anonymous volume is now unused. Once the new container works, remove it:
docker volume ls
docker volume prune -fdocker volume prune removes unused anonymous volumes only. Named volumes stay unless you add -a.