You do not update a container — you replace it. Pull the new image, stop the old container, start a new one from the new image. Anything not on a volume is gone in that step, which is the whole thing to understand.
There is a docker update command and it does not do what the name suggests. It changes resource limits on a running container — memory, CPU, restart policy — and nothing about the image.
Updating the software means replacing the container, and the reason that feels wrong at first is the reason containers are useful: they are immutable. The image is a fixed artifact, and a running container is a disposable instance of it.
Table of contents
- What docker update actually does
- The correct sequence for a standalone container
- With Compose, it is two commands
- Data survives only if it is on a volume
- Tags, digests, and knowing what you deployed
- Updating without downtime
- How this fits the rest of the stack
- FAQ
What docker update actually does
docker update --memory 1g --cpus 2 my-container
docker update --restart unless-stopped my-container
That is the whole scope: resource constraints and restart policy on an existing container, applied without restarting it. Genuinely useful when a container is being OOM-killed and you want to raise the limit without downtime.
It cannot change the image, environment variables, port mappings, mounted volumes or the command. Those are fixed at creation and changing them requires a new container.
So docker update is a resource-tuning command with a confusing name. The thing people are looking for is the sequence in the next section.
The correct sequence for a standalone container
# 1. Pull the new image
docker pull nginx:1.27
# 2. Note how the current one was created, if you did not write it down
docker inspect my-nginx
# 3. Stop and remove
docker stop my-nginx
docker rm my-nginx
# 4. Recreate with the same flags and the new image
docker run -d --name my-nginx \
-p 80:80 \
-v /srv/nginx/conf:/etc/nginx/conf.d \
--restart unless-stopped \
nginx:1.27
Step 4 is where this goes wrong, because you must reproduce every flag the original was created with. Miss a volume and the data is not deleted but the new container cannot see it, which looks identical to data loss until you work out what happened.
This is the strongest argument for Compose over docker run: the flags live in a file rather than in your shell history.
Note that pulling a tag does not affect running containers. nginx:1.27 now resolves to a new image locally, but the running container is still using the image ID it started with. That is correct behaviour and occasionally surprising.
With Compose, it is two commands
docker compose pull
docker compose up -d
Compose compares each service’s desired state against what is running and recreates only what changed. Services whose images did not change are left alone.
Useful variations:
docker compose pull api && docker compose up -d api # one service
docker compose up -d --force-recreate # rebuild regardless
docker compose up -d --build # rebuild locally-built images first
Because the volumes, ports and environment are declared in docker-compose.yml, the recreated container gets them automatically. That removes the entire class of mistake from the previous section, which is reason enough to use Compose even for a single container.
Data survives only if it is on a volume
This is the part that costs people real data. Anything written to the container’s own filesystem disappears when the container is removed. Only named volumes and bind mounts persist.
services:
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data # named volume -- survives
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
volumes:
pgdata:
Before updating anything stateful, check what is actually persisted:
docker inspect -f '{{ range .Mounts }}{{ .Type }} {{ .Source }} -> {{ .Destination }}{{ "\n" }}{{ end }}' my-container
If a database’s data directory does not appear in that output, it is inside the container and one docker rm from being gone. Fix that before updating, not after.
And back up before major version upgrades regardless. Postgres 16 will not start on a Postgres 15 data directory, and discovering that after removing the old container is a bad afternoon.
Tags, digests, and knowing what you deployed
latest is not a version. It is a mutable tag that whoever publishes the image can repoint at any time, so two deploys of myapp:latest a week apart can be different software with no record of the change.
Pin to specific versions in anything you care about:
image: postgres:16.2 # good
image: postgres:latest # not a version
image: postgres@sha256:abc123... # exact, immutable
A digest pin is truly immutable — the same bytes forever — at the cost of updating it by hand. A common middle ground is pinning the minor version and reviewing upgrades deliberately.
Two useful habits: docker image inspect --format '{{.Id}}' to record exactly what is running, and cleaning up afterwards, because old images accumulate quickly:
docker image prune -a --filter "until=168h" # unused images older than a week
Updating without downtime
The stop-then-start sequence has a gap where nothing is serving. For a personal service that is fine. For anything users touch, the pattern is to start the replacement before stopping the original — start a new container, wait for it to pass a health check, switch traffic, then remove the old one.
Doing that by hand means a reverse proxy, a health endpoint and a switching step, which is why orchestration exists. It is a real amount of machinery for what is conceptually a simple goal.
On RunxBuild, Docker services deploy from a repository: push, get a build log, and the new version takes over with a live route, runtime logs and rollback to a previous deploy. Persistent storage attaches to the service, so it is not part of what gets replaced — which removes the volume mistake described above by construction, and makes a bad update a rollback rather than a rebuild from shell history.
How this fits the rest of the stack
docker update changes resource limits; updating software means pull, stop, remove, recreate — with every original flag reproduced, which is why Compose is worth using even for one container. Keep state on named volumes, pin real versions rather than latest, and back up before major upgrades. For anything users depend on, a platform that replaces the container behind a health check and keeps a rollback is the honest answer; the RunxBuild hosting calculator shows a Docker service with persistent storage as line items.
Useful related references:
- Installation of Docker in Ubuntu: Step-by-Step for 22.04 and 24.04
- How to Install Docker on Ubuntu: 2026 Guide (22.04 and 24.04)
- Docker List Running Containers: Past docker ps
- Docker services on RunxBuild
FAQ
Does docker update change the image?
No. docker update only changes resource limits and the restart policy on an existing container. To run a new image you must pull it, stop and remove the old container, and create a new one.
How do I update a running Docker container?
Pull the new image, stop and remove the container, then recreate it with the same flags and the new image. With Compose it is docker compose pull followed by docker compose up -d, which recreates only the services whose images changed.
Will I lose my data when updating a container?
Only data written to the container’s own filesystem. Named volumes and bind mounts survive. Run docker inspect and confirm the data directory appears in the Mounts list before removing anything, and back up before major version upgrades.
Why is my container still running the old image after docker pull?
Pulling updates the local image but does not touch running containers, which continue using the image ID they started with. You have to recreate the container for the new image to take effect.
Should I use the latest tag?
No, for anything you care about. latest is a mutable tag the publisher can repoint at any time, so two deploys can silently be different software. Pin a specific version, or a digest for full immutability.