Skip to content

Conversation

@ibrahimypr
Copy link
Contributor

What I did
Until now, mustRecreate logic only checked for divergence in TypeVolume mounts but ignored TypeImage mounts. This inconsistency caused containers to erroneously retain stale images even after the source image was rebuilt.

This commit introduces a new label com.docker.compose.image.mounts that tracks the digests of images used as mounts. The convergence logic is updated to verify this label, ensuring that containers are correctly recreated whenever the underlying image mount digest changes.
Related issue
Fixes #13547

@ibrahimypr ibrahimypr requested a review from a team as a code owner January 23, 2026 08:00
@ibrahimypr ibrahimypr requested review from glours and ndeloof January 23, 2026 08:00
Copy link
Contributor

@ndeloof ndeloof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan about using (yet another) labels for this.
As an alternative, I suggest compose could resolve digest for image used as volume source, and update service definition accordingly (comparable to docker compose config --resolve-image-digests). Doing so we get a service definition that can be compared with actual container state.

@ibrahimypr ibrahimypr force-pushed the fix/image-mount-recreate branch from 38a9962 to 12e2cd1 Compare January 23, 2026 18:02
@ibrahimypr
Copy link
Contributor Author

Thanks for the feedback! I've updated the implementation to resolve the image digest directly in the volume source.
This way, ServiceHash naturally detects the change without needing an extra label.
Updated the tests as well.

@ibrahimypr ibrahimypr requested a review from ndeloof January 23, 2026 18:06
}
if img, ok := images[imgName]; ok {
// Append digest to source so config hash changes when image is rebuilt
service.Volumes[i].Source = fmt.Sprintf("%s@%s", vol.Source, img.ID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better use of reference.ParseDockerRef() then reference.WithDigest() to enforce format follows expectations

"gotest.tools/v3/assert"
)

func TestMustRecreateImageMounts(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we prefer use of e2e test on docker/compose as those allows to detect actual issues with the engine API.

@ibrahimypr ibrahimypr force-pushed the fix/image-mount-recreate branch from 12e2cd1 to ac49d00 Compare January 26, 2026 09:09
@ibrahimypr ibrahimypr requested a review from ndeloof January 26, 2026 09:09
@ibrahimypr
Copy link
Contributor Author

Sorry for the amateur mistakes, this is my first open source contribution!
I've implemented the requested changes: used reference.WithDigest for proper formatting and replaced the unit test with an E2E test case. Thanks for guiding me through it!

@ibrahimypr ibrahimypr force-pushed the fix/image-mount-recreate branch from ac49d00 to 193247d Compare January 26, 2026 09:11
ndeloof
ndeloof previously approved these changes Jan 26, 2026
@ibrahimypr
Copy link
Contributor Author

Fixed subpath in e2e test fixture (must be relative path) causing daemon error. Also addressed linter feedback.

return nil
}

func resolveImageVolumes(service types.ServiceConfig, images map[string]api.ImageSummary, projectName string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder this works as expected as you don't pass a *types.ServiceConfig so struct is copied in function scope, leaving original unmodified.

Until now, mustRecreate logic only checked for divergence in TypeVolume
mounts but ignored TypeImage mounts. This inconsistency caused containers
to erroneously retain stale images even after the source image was rebuilt.
This commit updates ensureImagesExists to resolve image volume sources to
their digests using the official reference package. This enables ServiceHash
(config hash) to naturally detect underlying image digest changes,
triggering recreation via the standard convergence logic.
An E2E test case is added to verify this behavior.
Fixes docker#13547

Signed-off-by: ibrahim yapar <74625807+ibrahimypr@users.noreply.github.com>
@ibrahimypr ibrahimypr force-pushed the fix/image-mount-recreate branch from 822f608 to 3463651 Compare January 26, 2026 14:00
@ibrahimypr
Copy link
Contributor Author

Thanks again for the review!

  1. Fixed Struct Copy: You were spot on about the struct copy issue—I've updated resolveImageVolumes to accept *types.ServiceConfig so modifications apply correctly.
  2. Local Image & CI: I noticed the E2E tests for local builds were failing with No such image when using the name@digest format (via reference.WithDigest), likely because locally built images in CI don't always have valid repo references. To fix this, I switched to using the Image ID (sha256:...) directly as the volume source. This reliably triggers the ServiceHash change on rebuilds and works for both local and remote images.
    Please let me know if this implementation looks good or if you'd prefer a different approach for handling local image references!

@ndeloof ndeloof enabled auto-merge (rebase) January 26, 2026 14:45
@ndeloof ndeloof merged commit 56ab28a into docker:main Jan 26, 2026
24 checks passed
@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 63.63636% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/compose/build.go 63.63% 3 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Image mount holds on to stale image even once image is removed

2 participants