Updates fix security holes, but a careless update can break an app or damage its data. This guide gives you a short routine: pin versions, back up, update one app at a time, and know how to go back.
Do not skip updates
Never updating is the bigger risk. Security fixes only help once they run on your server. An old version of a public-facing app keeps every hole that was fixed after your install. If you followed Why you should not open ports, your apps sit behind a VPN or a tunnel. That lowers the risk. It does not remove it.
The goal is not to update less. The goal is to update on purpose, at a time you pick, with a way back.
Pin a version in the compose file
A Docker image tag names a version of an app. If you write image: someapp:latest, you get whatever the author last labelled latest. That can be a new major version with changes that need a migration. A migration rewrites the app's stored data into a new format.
You can see the effect on the official Postgres image. As of October 2026, the latest tag points to Postgres 18, and version 18 moved the data folder and changed the volume path. A docker compose pull on a latest tag from an older install could hand you that change by surprise. latest can also lag: as of October 2026, Uptime Kuma's latest still points to version 1, while its 2 tag carries the current release.
Pin the tag instead. Most images offer two levels:
- A major tag, such as
louislam/uptime-kuma:2. It follows fixes inside version 2 and stops at version 3. The Uptime Kuma project's own compose file uses this tag. - A full tag, such as
louislam/uptime-kuma:2.5.8. It never moves. Both tags exist on Docker Hub as of October 2026.
For a novice, the major tag is a sane default for most apps. Use a full tag for databases and for any app where you want to choose each update yourself. Check the real tag names on the app's registry page, because projects name them differently. A major tag is a promise by the project, not a guarantee, so the routine below still applies.
services:
uptime-kuma:
# previous: louislam/uptime-kuma:2.5.7
image: louislam/uptime-kuma:2.5.8
The comment is your rollback note. Docker Compose basics covers the file itself.
Read the release notes first
Before you change a tag, read what changed. Most projects publish notes on the GitHub Releases page of the repository, and some keep a separate changelog. Look for words like "breaking", "migration", "removed" and "required".
The Uptime Kuma 2.0 migration guide shows why. It tells you to back up the data folder first. It warns that the migration can take hours on slow hardware or with many monitors, and that an interrupted migration means restoring from your backup. Uptime Kuma is a good example of an app that is easy to run but needs care on a major jump.
Back up before every update
Many migrations cannot be undone. The new version changes the data, and the old version may no longer read it. Swapping the old tag back does not restore the old data. Only a backup does.
The first step of every update is a fresh backup that you know how to restore. Backups you can restore explains how. For a simple app with files in a folder, such as Uptime Kuma's ./data, stop the app first and copy the folder:
docker compose stop
sudo tar czf ../uptime-kuma-data-$(date +%F).tar.gz ./data
Do not skip it because the update looks small. Small updates carry migrations too.
The update routine
Do this for one app at a time, from the folder that holds its compose file:
-
Read the release notes.
-
Back up, as above.
-
Write the current tag down. A comment in the compose file works.
-
Edit the
image:line to the new tag. -
Pull the new image:
docker compose pull -
Start it:
docker compose up -dup -dstops and recreates the containers whose image changed and keeps your mounted volumes. -
Watch the logs and open the app:
docker compose logs --tail 50 -fStop following with Ctrl+C. Look for errors and for migration messages. Log in and check that your data is there.
If the app fails before it migrates anything, set the old tag back and run docker compose up -d again. If the logs show a migration ran, or you are unsure, restore the backup first, then set the old tag. Without the restore, the old version may refuse to start or may corrupt data.
Database engines need a dump and restore
A database engine is a program such as Postgres that many apps use to store data. Changing its tag from 16 to 17 is not an update. The new version refuses to start on the old data folder and logs database files are incompatible with server.
The Postgres manual says that for major releases the internal data storage format is subject to change. It describes three ways to upgrade: a dump and restore with pg_dumpall, an in-place pg_upgrade, and an upgrade through replication.
For a home server, the dump and restore is the easiest to follow:
- Stop the apps that use the database.
- Dump everything from the old server with
pg_dumpall. - Start a new, empty Postgres container on the new major version, with a new data folder.
- Load the dump into it.
- Start the apps and check them.
Keep the old data folder until you are sure. Read the upgrade notes of the app that uses the database. For version 18 and later, mount the volume where the image page says.
Minor updates inside one major version, such as 17.4 to 17.5, keep the same storage format. Those need no dump and restore, only the normal backup and pull.
Auto-updaters surprise novices
An auto-updater pulls new images and restarts containers without asking you. That cuts the backup and release notes out of the process. The first time it updates a database or crosses a major version, you find out from a broken app.
Watchtower is the best-known one. As of October 2026, its repository is archived and read-only since 17 December 2025, and the maintainers wrote that they are no longer maintaining it and cannot vouch for forks. Do not start a new setup with it.
If you already run it, limit what it does:
- Set
WATCHTOWER_MONITOR_ONLYso it checks and notifies but does not update. You can also set thecom.centurylinklabs.watchtower.monitor-onlylabel on single containers. - Use
WATCHTOWER_LABEL_ENABLEso it touches only containers with the labelcom.centurylinklabs.watchtower.enable=true. - Never let it touch a database.
For a "tell me" tool, use Diun. It checks image tags on a schedule and sends a notification when one changes, through channels such as Telegram, Discord, email and ntfy. It does not update anything. Its Docker provider watches only containers with the diun.enable=true label by default. The project is still active on GitHub as of October 2026.
Many apps also show their own "update available" notice. That notice costs nothing, so read it, then run the routine.
Update the server too
The host operating system needs updates as well. On Debian 13 and Ubuntu 24.04, unattended-upgrades installs security updates for you. On Ubuntu, it runs once per day and applies security updates by default, with automatic reboots off. On Debian, check that it is on:
sudo dpkg-reconfigure unattended-upgrades
First hour on a new server sets this up.
Some updates, mainly kernel updates, need a reboot to take effect. The system marks this with the file /var/run/reboot-required. Check it when you log in:
ls /var/run/reboot-required
If the file exists, pick a quiet moment and run sudo reboot. Containers with restart: unless-stopped start again by themselves once Docker is back. You can let the server reboot itself with Unattended-Upgrade::Automatic-Reboot, but it then reboots without asking you, so set a quiet hour with Automatic-Reboot-Time if you choose that.
Clean up old images
Each update leaves the old image on disk. Over months, this fills a small VPS disk. Remove images that no container uses:
docker image prune -a
By default, docker image prune removes only dangling images, those without a tag. The -a flag also removes every image that no container uses. That includes the old image you may need for a quick rollback. Prune after an update has proved stable, not before.
Update checklist
- [ ] Read the release notes for breaking changes.
- [ ] Back up, and know how to restore.
- [ ] Write the current tag down.
- [ ] Change the tag in the compose file, one app at a time.
- [ ]
docker compose pull, thendocker compose up -d. - [ ] Read the logs and open the app.
- [ ] Roll back with the backup if a migration ran and the app is broken.
- [ ] Check
/var/run/reboot-requiredon the host. - [ ] Prune old images once the update is stable.
Next step: Know when it breaks shows how to find out about a broken update before your users do.