Docker Compose Update: A Complete Guide to Updating Services Safely
Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.
Keeping containers current is one of those tasks that seems trivial until it breaks a production stack at the worst possible moment. This guide walks through how to perform a docker compose update correctly, covering image pulls, dependency ordering, rollback strategy, and the common pitfalls that turn a routine update into an incident.
Whether you’re managing a single VPS running a handful of services or a small fleet of hosts, understanding exactly what happens during a docker compose update — and what doesn’t happen automatically — is essential to running a reliable stack.
Why a Docker Compose Update Is Different From a Simple Restart
A common misconception is that restarting a service refreshes its image. It doesn’t. Docker Compose caches image references locally, and unless you explicitly pull new layers, docker compose restart will simply relaunch the exact same container from the exact same image digest. A real docker compose update requires three distinct steps: pulling new image data, recreating containers that reference changed images, and — where applicable — running any migration or initialization logic those new images expect.
This distinction matters because teams often assume their stack is “up to date” simply because it’s been restarted periodically. In reality, an image tagged latest in your docker-compose.yml can sit unchanged on disk for months unless a pull is triggered on purpose.
The Three Phases of an Update
Every meaningful docker compose update goes through the same three phases, regardless of stack size:
1. Pull — fetch new image layers from the registry.
2. Recreate — stop old containers and start new ones from the updated images, preserving volumes and networks.
3. Verify — confirm the new containers are healthy and the application behaves correctly.
Skipping verification is the single most common cause of “silent” outages — a container starts, looks running, but the application inside is crash-looping or serving errors.
The Standard Docker Compose Update Workflow
The most common and safest pattern for a docker compose update looks like this:
cd /opt/myapp
docker compose pull
docker compose up -d
docker compose ps
docker compose pull fetches the latest images for every service defined with a tag (not a locally built build: context). docker compose up -d then compares the new image IDs against the running containers and recreates only the ones that changed — services with unchanged images are left untouched, which avoids unnecessary downtime for parts of the stack that didn’t need it.
Updating a Single Service Instead of the Whole Stack
You rarely want to update everything at once, especially in a multi-service stack where a database and an application share a compose file. Targeting a single service keeps the blast radius small:
docker compose pull redis
docker compose up -d redis
This pulls and recreates only the redis service, leaving every other container — and its network connections — untouched. This pattern is especially useful when you’re testing a docker compose update against a single component before rolling it out stack-wide.
Forcing Recreation Without a New Image
Occasionally you need to force a container recreation even when the image hasn’t changed — for example, after editing environment variables or volume mounts in docker-compose.yml. The --force-recreate flag handles this:
docker compose up -d --force-recreate app
This is a distinct scenario from a true docker compose update, but it’s worth knowing the difference: pull changes the image, --force-recreate changes the container regardless of the image.
Handling Stateful Services During an Update
Updating stateless application containers is usually low-risk. Updating stateful services — databases, message queues, search indexes — carries real risk, because a new major version may expect a different on-disk data format.
postgres:16.4 rather than postgres:16) so an unrelated docker compose pull doesn’t silently jump a version.If you’re running Postgres in this kind of setup, the Postgres Docker Compose setup guide covers volume and backup conventions that make this kind of update far less risky. The same logic applies to Redis and other stateful services — know what’s persisted, and where, before you touch the image tag.
Version Pinning: The Most Important Habit for Safe Updates
Nothing causes more unpredictable docker compose update behavior than floating tags. If your docker-compose.yml references nginx:latest or node:20, a docker compose pull can bring in a genuinely different image than the one you tested last week, with no changelog forcing you to notice.
services:
web:
image: nginx:1.27.0
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
Pinning to a specific tag turns every docker compose update into a deliberate, reviewable action: you change the tag in the YAML file, commit that change, and only then run the pull/recreate cycle. This also makes rollback trivial — revert the tag in version control and re-run the same two commands.
Diffing Before You Pull
If you’re not sure whether an update will pull anything new, docker compose pull --dry-run (available in recent Compose versions) or simply comparing digests avoids surprises:
docker compose images
docker compose pull
docker compose images
Comparing the two images outputs tells you exactly which services actually received new image IDs, which is far more reliable than assuming a pull did nothing just because the command returned quickly.
Rebuilding Locally Built Images as Part of an Update
If a service is built from a local Dockerfile rather than pulled from a registry, docker compose pull does nothing for it — you need to rebuild:
docker compose build --no-cache app
docker compose up -d app
For projects that mix built and pulled services, a combined update command covers both cases:
docker compose build
docker compose pull
docker compose up -d
If you’re unclear on when to reach for a Dockerfile versus relying purely on Compose-managed images, the Dockerfile vs Docker Compose comparison and the more detailed Docker Compose rebuild guide are useful references for getting this workflow right before automating it.
Zero-Downtime Considerations
Standard docker compose up -d briefly stops the old container before starting the new one. For most internal tools and low-traffic services this brief gap is inconsequential, but for anything user-facing it’s worth understanding the limitation: Compose alone does not provide rolling updates or health-check-gated traffic cutover the way an orchestrator like Kubernetes does.
If your stack genuinely needs zero-downtime deploys, options include:
docker service update), which does support rolling updates natively.For most small-to-medium VPS deployments, a brief restart window during a scheduled docker compose update is an acceptable tradeoff against the added complexity of a full orchestrator.
Health Checks as an Update Safety Net
Defining a healthcheck in your compose file means Docker itself will report a container as unhealthy if the application fails to start correctly after an update, rather than just showing Up:
services:
app:
image: myapp:2.3.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
Checking docker compose ps after an update and confirming every service shows healthy rather than just running is a simple habit that catches a large share of failed updates immediately, instead of hours later.
Rolling Back a Failed Update
Because Compose doesn’t retain previous container state after up -d recreates a service, rollback means reverting the image reference and re-running the same commands:
git checkout HEAD~1 -- docker-compose.yml
docker compose up -d
This is exactly why pinned, version-controlled tags matter — without them, “the previous version” isn’t a well-defined thing you can roll back to. If your compose file references secrets or environment-specific values, keeping those separated from the image tag itself (see the Docker Compose secrets guide and the Docker Compose env guide) keeps a rollback limited to just the image change, rather than dragging configuration changes along with it.
Automating Docker Compose Updates Safely
Fully automated updates (cron-triggered pull + up -d) are tempting but risky for anything stateful or user-facing, since there’s no human checkpoint before a bad image reaches production. A safer middle ground:
pull step and alert on new image availability, but leave up -d as a manual or approval-gated step.docker compose logs (see the Docker Compose logs debugging guide) immediately after an automated update is a reasonable minimum safeguard.If you’re running this kind of workflow on a lightweight VPS rather than a managed platform, providers like DigitalOcean or Hetzner are common, low-overhead choices for hosting a single-host Compose stack that you update on a schedule.
Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.
FAQ
Does docker compose up -d automatically pull new images?
No. up -d only recreates containers if it detects a locally cached image ID differs from what’s currently running. If you never run docker compose pull (or use a build step), up -d will keep using whatever image is already on disk, even for a floating tag like latest.
How do I update just one service without touching the rest of the stack?
Pass the service name to both commands: docker compose pull <service> followed by docker compose up -d <service>. Compose will leave every other running container untouched, including shared networks and volumes.
Is docker compose restart the same as an update?
No. restart stops and starts the existing container from its existing image — it never pulls or rebuilds anything. A real docker compose update always requires either pull or build before up -d for the change to take effect.
What’s the safest way to update a database container?
Pin the image to an exact version, back up the volume first, review the target version’s release notes for breaking changes, and test the update against a copy of the data before touching production. Never rely on a floating major-version tag for a stateful service.
Conclusion
A reliable docker compose update comes down to a small set of disciplined habits: pin your image tags instead of floating on latest, pull deliberately rather than assuming a restart refreshes anything, treat stateful services with extra caution and a backup step, and verify health after every update rather than trusting that “running” means “working.” None of this requires exotic tooling — the built-in pull, build, and up -d commands are sufficient for the vast majority of single-host and small-fleet deployments. For teams eventually needing rolling updates with zero downtime, that’s the point to evaluate Swarm or Kubernetes rather than trying to force that behavior out of Compose alone. For the full command reference and flags used throughout this guide, the official Docker Compose CLI reference is the authoritative source to keep bookmarked.
Leave a Reply