How to Update Docker Compose Apps Safely With Rollbacks
Move a Docker Compose app to a newer image version without crossing your fingers, losing track of the old version, or getting stuck when the update goes bad.
How to pin versions, snapshot the current state, update deliberately, verify the result, and roll back quickly if needed.
Self-hosted apps, dashboards, admin tools, and small production stacks running on a VPS.
The most dangerous updates are the ones where you do not know exactly which version you were on before you changed anything.
Before you begin
- Docker Engine and the Compose plugin installed.
- A Compose app directory such as
~/apps/myapp. - Persistent volumes already in place for data you care about.
- A backup or export path for important databases and uploads.
Updating a Compose app should feel boring. If it feels dramatic, your process is too loose. The goal is simple: know what is running now, know what you are changing to, and keep a clean path back to the old version if the new one misbehaves.
Step 1: Build a rollback baseline before you touch anything
Start inside the app directory and collect the facts you will want later:
cd ~/apps/myapp
docker compose ps
docker compose images
docker compose config > compose.rendered-before-update.ymlIf your Compose file uses floating tags like latest, fix that first. Pin explicit versions instead:
services:
app:
image: yourorg/yourapp:1.14.2Save a copy of the current Compose file and env file:
cp compose.yml compose.yml.bak-$(date +%F-%H%M)
cp .env .env.bak-$(date +%F-%H%M)For stateful apps, take a backup that matches the app. Examples:
# Postgres example
docker compose exec -T db pg_dump -U myapp myapp > backup-before-update.sql
# App uploads example
tar -czf uploads-before-update.tar.gz ./data/uploadsExpected outcome: You know the current image version, you have the current config rendered to disk, and you have a recoverable copy of important data.
Step 2: Review the update before pulling anything
Read the image or app release notes. Look for database migrations, renamed environment variables, permission changes, and breaking config changes. Then update the tag in compose.yml or .env if you store image versions there.
Validate the final config:
docker compose configPre-pull the new image without restarting yet:
docker compose pullThis is useful because pull errors are much less stressful before the app is touched.
Step 3: Run the update safely
When you are ready, recreate the services in detached mode:
docker compose up -dThen verify more than just “container started”:
docker compose ps
docker compose logs --tail=100
curl -I http://localhost:8080If the app has a login, dashboard, webhook, or worker queue, test the actual feature path too. A green container is not the same thing as a healthy application.
For a multi-service stack, it is often smart to update one app at a time when possible:
docker compose pull app
docker compose up -d appThat narrows the blast radius and makes troubleshooting easier.
Step 4: Roll back if the update breaks
If the new version fails, do not improvise. Revert the image tag or restore the backed-up Compose files:
cp compose.yml.bak-2026-05-01-1530 compose.yml
cp .env.bak-2026-05-01-1530 .envOr manually set the previous image version again, then redeploy:
docker compose up -dIf the app also changed data structures and cannot start cleanly after the image rollback, stop and restore data from the backup you created earlier. For example:
docker compose down
mv ./data/uploads ./data/uploads.bad
mkdir -p ./data/uploads
tar -xzf uploads-before-update.tar.gz -C ./data/uploads --strip-components=1Recovery notes:
- If the new release ran irreversible database migrations, restoring the old image alone may not be enough.
- If you use bind mounts, confirm file ownership and permissions after restoring.
- If you use named volumes, verify you did not accidentally recreate them under a different project name.
Troubleshooting common update problems
The container will not start after the update.
Run docker compose logs --tail=200 and look for missing env variables, bad permissions, or migration failures.
The app starts, but the UI is broken.
Clear browser cache, confirm static assets rebuilt correctly, and check whether the release changed a base URL, proxy setting, or CSP rule.
The old version will not come back cleanly.
Check release notes for schema changes. You may need to restore the database backup as well as the old image version.
Compose pulled a different image than expected.
Use explicit version tags, not latest. Re-run docker compose images to verify what is actually deployed.
What to do next
Once your update process is safe, the next improvement is making service health easier to reason about. Continue with Docker Compose Healthchecks, Restart Policies, and Resource Limits.
