Summary
Scheduled auto-update never recreates containers. Every run logs No updates for stack '<name>', even when the pull actually fetched a newer image, so stacks quietly stay on their old images forever and the superseded images pile up in the Docker tab as "orphan images" (dangling but in use).
Version: 2026.07.02, Docker Compose v5.1.2
Root cause
scripts/compose_autoupdate.sh decides whether to run up -d by comparing image digests before and after the pull:
get_image_digests() {
docker compose "${project_dir_args[@]}" "${compose_file_args[@]}" "${env_file_args[@]}" -p "$PROJECT_NAME" images -q 2>/dev/null | while read -r img_id; do
if [ -n "$img_id" ]; then
docker inspect --format='{{index .RepoDigests 0}}' "$img_id" 2>/dev/null || echo "$img_id"
fi
done | sort
}
OLD_DIGESTS=$(get_image_digests || true)
... docker compose ... pull --ignore-buildable ...
NEW_DIGESTS=$(get_image_digests || true)
if [ "$OLD_DIGESTS" != "$NEW_DIGESTS" ]; then
... up -d ...
The problem: docker compose images lists the images of the existing containers, and a pull never touches containers. So OLD_DIGESTS and NEW_DIGESTS are always equal and the up -d branch is unreachable. The pull still moves the tag, which is what strands the running containers on untagged images.
Reproduction
- Take any stack with auto-update enabled whose service uses a mutable tag (e.g.
nginx:mainline-alpine).
- Simulate an available update with
docker tag <old-image-id> nginx:mainline-alpine, or just wait for upstream to publish a new image.
- Run
compose_autoupdate.sh for the stack, or wait for the schedule.
- The pull repoints the tag to the newer image, the script logs
No updates for stack '<name>', and the container keeps running the old image, which is now dangling.
Suggested fix
Read the digests from the compose config's image references instead of from the containers:
get_image_digests() {
docker compose "${project_dir_args[@]}" "${compose_file_args[@]}" "${env_file_args[@]}" -p "$PROJECT_NAME" config --images 2>/dev/null | while read -r img_ref; do
if [ -n "$img_ref" ]; then
docker image inspect --format='{{index .RepoDigests 0}}' "$img_ref" 2>/dev/null || echo "$img_ref"
fi
done | sort
}
Two changes in there:
images -q → config --images: digests now come from whatever the tag points at, which is exactly what pull updates. Before the first-ever pull of an image the inspect fails and falls back to echoing the ref, so a fresh pull also registers as a change, which is what you want. Locally built services have no RepoDigests, fall back to the (stable) ref, and get skipped, consistent with pull --ignore-buildable.
docker inspect → docker image inspect: config --images yields names, and a bare docker inspect <name> can collide with a container of the same name.
I've been running this patch and it works: with a stack whose tag deliberately pointed at an older image, the patched script pulled, saw the digest change, ran up -d, and recreated the stack's containers onto the current images. When nothing has changed it still reports No updates.
Summary
Scheduled auto-update never recreates containers. Every run logs
No updates for stack '<name>', even when the pull actually fetched a newer image, so stacks quietly stay on their old images forever and the superseded images pile up in the Docker tab as "orphan images" (dangling but in use).Version: 2026.07.02, Docker Compose v5.1.2
Root cause
scripts/compose_autoupdate.shdecides whether to runup -dby comparing image digests before and after the pull:The problem:
docker compose imageslists the images of the existing containers, and apullnever touches containers. SoOLD_DIGESTSandNEW_DIGESTSare always equal and theup -dbranch is unreachable. The pull still moves the tag, which is what strands the running containers on untagged images.Reproduction
nginx:mainline-alpine).docker tag <old-image-id> nginx:mainline-alpine, or just wait for upstream to publish a new image.compose_autoupdate.shfor the stack, or wait for the schedule.No updates for stack '<name>', and the container keeps running the old image, which is now dangling.Suggested fix
Read the digests from the compose config's image references instead of from the containers:
Two changes in there:
images -q→config --images: digests now come from whatever the tag points at, which is exactly whatpullupdates. Before the first-ever pull of an image the inspect fails and falls back to echoing the ref, so a fresh pull also registers as a change, which is what you want. Locally built services have noRepoDigests, fall back to the (stable) ref, and get skipped, consistent withpull --ignore-buildable.docker inspect→docker image inspect:config --imagesyields names, and a baredocker inspect <name>can collide with a container of the same name.I've been running this patch and it works: with a stack whose tag deliberately pointed at an older image, the patched script pulled, saw the digest change, ran
up -d, and recreated the stack's containers onto the current images. When nothing has changed it still reportsNo updates.