This issue was filed by an AI agent on a human's behalf. The human submitter may not have independently verified the report.
Description
On an Engine using the containerd image store, Compose 5.4.0 can recreate a container on the second identical docker compose up when the first up had to pull a multi-manifest image.
Current behavior:
- The first
up pulls the missing image and creates the container.
- The second identical
up, without another pull or any configuration/image change, reports the service as Recreate and replaces the container.
Expected behavior:
- The second
up recognizes that the image and configuration are unchanged and leaves the container running.
This can destroy state held in the container writable layer. We encountered it with a stateful infrastructure container initialized between the two Compose invocations.
Steps To Reproduce
Use Docker Engine with the containerd image store and API >= 1.48. Start with the selected multi-platform image absent locally.
services:
app:
image: alpine:3.22.2
command: ["sleep", "infinity"]
docker image rm alpine:3.22.2 # only to make the first Compose invocation pull
docker compose up -d
first_id=$(docker compose ps -q app)
docker inspect -f '{{index .Config.Labels "com.docker.compose.image"}}' "$first_id"
docker compose up -d
second_id=$(docker compose ps -q app)
test "$first_id" = "$second_id"
On the affected setup, the second up prints Recreate and the final comparison fails.
Compose Version
Docker Compose version v5.4.0
Docker Environment
Docker Engine 29.7.2
API version 1.55
Storage Driver: overlayfs
driver-type: io.containerd.snapshotter.v1
containerd 2.3.3
linux/amd64
Anything else?
This appears to be a digest-type inconsistency introduced when getImageSummaries was changed by fd794ea to select the platform runnable-manifest digest:
- During the initial pull,
pullRequiredImages stores the value returned by pullServiceImage in the project image map.
pullServiceImage currently returns plain ImageInspect.ID. With the containerd image store, this can be the top-level image-index digest. That value becomes the new container's com.docker.compose.image label.
- During the next
up, the image is already local. getImageSummaries requests manifests and contentDigest returns the platform runnable-manifest digest.
- Reconciliation compares the runnable-manifest digest with the existing container's index-digest label and treats the unchanged image as stale.
In the observed failure, the existing container label held sha256:46302bcb91a7e8008e6394be8afafdbfa40fb77a54d4046a38be35992042d5de, while the later local-image lookup selected a different platform manifest digest for the same unchanged tag. The Compose configuration hash and mounts were identical, and no pull occurred during the second invocation.
Compose 5.1.4 does not reproduce this because both paths use plain inspect.ID. A patch that resolves the post-pull image through the same getImageSummaries path fixes the mismatch.
This is related to the containerd multi-manifest handling in #14007, but it is a different path and symptom: #14007 rejects a locally available non-native platform and attempts a pull; this issue successfully pulls a same-platform image and then unnecessarily recreates it on the following up.
This issue was filed by an AI agent on a human's behalf. The human submitter may not have independently verified the report.
Description
On an Engine using the containerd image store, Compose 5.4.0 can recreate a container on the second identical
docker compose upwhen the firstuphad to pull a multi-manifest image.Current behavior:
uppulls the missing image and creates the container.up, without another pull or any configuration/image change, reports the service asRecreateand replaces the container.Expected behavior:
uprecognizes that the image and configuration are unchanged and leaves the container running.This can destroy state held in the container writable layer. We encountered it with a stateful infrastructure container initialized between the two Compose invocations.
Steps To Reproduce
Use Docker Engine with the containerd image store and API >= 1.48. Start with the selected multi-platform image absent locally.
On the affected setup, the second
upprintsRecreateand the final comparison fails.Compose Version
Docker Environment
Anything else?
This appears to be a digest-type inconsistency introduced when
getImageSummarieswas changed by fd794ea to select the platform runnable-manifest digest:pullRequiredImagesstores the value returned bypullServiceImagein the project image map.pullServiceImagecurrently returns plainImageInspect.ID. With the containerd image store, this can be the top-level image-index digest. That value becomes the new container'scom.docker.compose.imagelabel.up, the image is already local.getImageSummariesrequests manifests andcontentDigestreturns the platform runnable-manifest digest.In the observed failure, the existing container label held
sha256:46302bcb91a7e8008e6394be8afafdbfa40fb77a54d4046a38be35992042d5de, while the later local-image lookup selected a different platform manifest digest for the same unchanged tag. The Compose configuration hash and mounts were identical, and no pull occurred during the second invocation.Compose 5.1.4 does not reproduce this because both paths use plain
inspect.ID. A patch that resolves the post-pull image through the samegetImageSummariespath fixes the mismatch.This is related to the containerd multi-manifest handling in #14007, but it is a different path and symptom: #14007 rejects a locally available non-native platform and attempts a pull; this issue successfully pulls a same-platform image and then unnecessarily recreates it on the following
up.