A new version of your image landed in the registry, but the container running on your host is still on the old one. Restarting it won't help, because a container is bound to the exact image it was created from and keeps using it for its entire lifecycle. Docker has no concept of patching a container in place; upgrading always means replacing the container with a fresh one built from the new image. This article walks through doing that for a standalone container, for Docker Compose, and with Watchtower for automatic updates.
Why there's no in-place upgrade
When Docker starts a container, it stacks a thin writable layer on top of the image's read-only layers. The image is frozen at the digest it had when you ran docker run. Pulling a newer image with the same tag changes what's on disk, but it doesn't reach into the already-running container and swap its layers. That container goes on referencing the old image until you stop and remove it.
This is deliberate. Immutable containers are reproducible: the thing running in production is byte-for-byte the thing you tested. The cost of that guarantee is that "upgrade" and "replace" are the same operation. If you want the difference between images and containers in more depth, that's its own topic, but the practical takeaway is short: the image is the unit of change, and the container is disposable.
Upgrade a standalone container
For a single container started with docker run, the upgrade is a four-step cycle: pull, stop, remove, run.
First, pull the new image. Use a specific version tag rather than latest so you know exactly what you're deploying:
1docker pull nginx:1.27
Watch the last line of the output to confirm something actually changed:
1Status: Downloaded newer image for nginx:1.27
If it instead says Status: Image is up to date for nginx:1.27, the registry has nothing new and there's no point recreating the container. This matters most with the latest tag, where the tag name stays the same but the underlying digest moves. The pull is what tells you whether an upgrade is even warranted.
Once you have the new image, stop and remove the old container, then start a replacement with the same options:
123docker stop webdocker rm webdocker run -d --name web -p 8080:80 nginx:1.27
The new container picks up the updated image. The one detail people miss here is that every flag from the original docker run has to be repeated: port mappings, volume mounts, environment variables, restart policies. Docker doesn't remember how the old container was configured, so anything you omit silently disappears from the new one. If your run commands are long enough that this is error-prone, that's a strong signal to move the service into Compose.
That manual cycle is the entire job for one container. The dedicated walkthrough for updating a running container covers it in isolation, so the rest of this article handles the parts a single docker run doesn't: rolling the change across a Compose stack and automating it with Watchtower.
Upgrade with Docker Compose
Docker Compose makes this far cleaner because the configuration lives in a file instead of in your shell history. The upgrade is two commands:
12docker compose pulldocker compose up -d
docker compose pull fetches the newest image for every service defined in your compose.yaml. docker compose up -d then compares each service's desired state against what's running and recreates only the containers whose image or configuration changed, leaving everything else untouched. Containers that are already current aren't disturbed, so you avoid needless restarts.
You can collapse this into a single command that always checks for new images before starting:
1docker compose up -d --pull always
Named volumes survive recreation, so a database service backed by a named volume keeps its data across the upgrade. And because Compose reapplies every setting from your file, you can't accidentally drop a port mapping or environment variable the way you can with a hand-written docker run.
Automate updates with Watchtower
If you don't want to run these commands by hand, Watchtower polls the registries for your running containers and upgrades them when it detects a new image digest. It connects to the Docker socket, pulls the new image, gracefully stops the old container, and starts a replacement reusing the original's configuration.
1234docker run -d \--name watchtower \-v /var/run/docker.sock:/var/run/docker.sock \containrrr/watchtower
By default it checks every 24 hours; set WATCHTOWER_POLL_INTERVAL (in seconds) or WATCHTOWER_SCHEDULE (a 6-field cron expression) to change that, and WATCHTOWER_CLEANUP=true to remove old images after each upgrade.
There's an important caveat here: the original containrrr/watchtower project was archived by its maintainer in December 2025 and is now read-only, with v1.7.1 as its final release. The image still works and is widely deployed, but it won't get new fixes. An actively maintained community fork exists at nicholas-fedor/watchtower (image nickfedor/watchtower) if you want ongoing releases. Either way, both the original authors and the fork recommend Watchtower for homelabs, media servers, and low-stakes environments rather than production, since unattended upgrades can pull a breaking change at 3 a.m. with no human in the loop.
Common pitfalls
The failure that bites people hardest is data loss. When you remove the old container, its writable layer goes with it. Anything the application wrote to the container filesystem rather than to a mounted volume is gone the moment you run docker rm. If your service writes state to a path like /app/data without a volume behind it, that state vanishes on every upgrade. Mount a volume for any data you care about before you start relying on this workflow.
The second common mistake is running docker compose up -d in a pipeline without pulling first. Plain up starts containers from the locally cached image, which can be days old, so your "upgrade" deploys nothing new. Always run docker compose pull first, or use --pull always, in any automated job.
Finally, tag confusion. Pinning to nginx:1.27 and never re-pulling means you'll never get patch releases, while tracking nginx:latest means a docker pull can swap the underlying image without warning. For anything you operate seriously, pin to a specific tag and upgrade it deliberately, so an image change is a decision you make rather than one that happens to you.
Final thoughts
Upgrading a Docker container is always a replace operation. The commands themselves rarely cause trouble. What trips people up is not noticing an image changed in the first place, or not knowing whether the replacement container actually came up healthy.
Dash0's infrastructure monitoring tracks container image versions and resource usage alongside real-time logs and distributed traces, so you can confirm an upgraded container is running the image you expect and behaving the way it should, all from one place.
Start a free trial today to monitor your containers, images, and deployments in a single view. No credit card required.