Skip to content

compose: recreate container when mounted image digest changes#13549

Merged
ndeloof merged 1 commit intodocker:mainfrom
ibrahimypr:fix/image-mount-recreate
Jan 26, 2026
Merged

compose: recreate container when mounted image digest changes#13549
ndeloof merged 1 commit intodocker:mainfrom
ibrahimypr:fix/image-mount-recreate

Conversation

@ibrahimypr
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Comment thread pkg/compose/build.go Outdated
}
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor Author

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

Comment thread pkg/compose/build.go Outdated
return nil
}

func resolveImageVolumes(service types.ServiceConfig, images map[string]api.ImageSummary, projectName string) {
Copy link
Copy Markdown
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 <[email protected]>
@ibrahimypr ibrahimypr force-pushed the fix/image-mount-recreate branch from 822f608 to 3463651 Compare January 26, 2026 14:00
@ibrahimypr
Copy link
Copy Markdown
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
Copy Markdown

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!

tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Feb 25, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [docker/compose](https://github.com/docker/compose) | minor | `v5.0.2` → `v5.1.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>docker/compose (docker/compose)</summary>

### [`v5.1.0`](https://github.com/docker/compose/releases/tag/v5.1.0)

[Compare Source](docker/compose@v5.0.2...v5.1.0)

#### What's Changed

##### 🐛 Fixes

- emit container status events after network reconnection (fixes [#&#8203;13524](docker/compose#13524)) by [@&#8203;MaheshThakur9152](https://github.com/MaheshThakur9152) in [#&#8203;13529](docker/compose#13529)
- Fix potential nil pointer dereference in container event monitoring by [@&#8203;Nepomuk5665](https://github.com/Nepomuk5665) in [#&#8203;13551](docker/compose#13551)
- compose: recreate container when mounted image digest changes by [@&#8203;ibrahimypr](https://github.com/ibrahimypr) in [#&#8203;13549](docker/compose#13549)
- fix panic by [@&#8203;ndeloof](https://github.com/ndeloof) in [#&#8203;13562](docker/compose#13562)
- Fix invalid path error when using OCI artifacts on Windows by [@&#8203;mikesir87](https://github.com/mikesir87) in [#&#8203;13574](docker/compose#13574)
- fix: execute post\_start hooks in docker compose run by [@&#8203;veeceey](https://github.com/veeceey) in [#&#8203;13607](docker/compose#13607)
- Fix panic when watch rebuilds without up by [@&#8203;maxproske](https://github.com/maxproske) in [#&#8203;13610](docker/compose#13610)

##### 🔧  Internal

- fsnotify is set in Dockerfile by [@&#8203;ndeloof](https://github.com/ndeloof) in [#&#8203;13533](docker/compose#13533)
- Dockerfile: update golangci-lint to v2.8.0 by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13535](docker/compose#13535)
- replace some uses of strings.Split(N) for strings.Cut by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13542](docker/compose#13542)
- Upgrade GitHub Actions to latest versions by [@&#8203;salmanmkc](https://github.com/salmanmkc) in [#&#8203;13546](docker/compose#13546)
- pkg/compose: remove dependency on github.com/docker/buildx/driver by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13563](docker/compose#13563)
- use Docker GitHub Builder to build and sign binaries and bin image by [@&#8203;crazy-max](https://github.com/crazy-max) in [#&#8203;13568](docker/compose#13568)
- ci: fix bin-image job by [@&#8203;crazy-max](https://github.com/crazy-max) in [#&#8203;13569](docker/compose#13569)
- pkg/compose: defaultNetworkSettings: refactor by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13580](docker/compose#13580)
- pkg/compose: un-export consts by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13584](docker/compose#13584)
- modernize some code by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13588](docker/compose#13588)

##### ⚙️ Dependencies

- update to go1.25.7 by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13573](docker/compose#13573)
- build(deps): bump golang.org/x/sys from 0.40.0 to 0.41.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13578](docker/compose#13578)
- build(deps): bump go.yaml.in/yaml/v4 from 4.0.0-rc.3 to 4.0.0-rc.4 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;13541](docker/compose#13541)
- migrate to moby modules by [@&#8203;thaJeztah](https://github.com/thaJeztah) in [#&#8203;13078](docker/compose#13078)

#### New Contributors

- [@&#8203;MaheshThakur9152](https://github.com/MaheshThakur9152) made their first contribution in [#&#8203;13529](docker/compose#13529)
- [@&#8203;salmanmkc](https://github.com/salmanmkc) made their first contribution in [#&#8203;13546](docker/compose#13546)
- [@&#8203;Nepomuk5665](https://github.com/Nepomuk5665) made their first contribution in [#&#8203;13551](docker/compose#13551)
- [@&#8203;ibrahimypr](https://github.com/ibrahimypr) made their first contribution in [#&#8203;13549](docker/compose#13549)
- [@&#8203;veeceey](https://github.com/veeceey) made their first contribution in [#&#8203;13607](docker/compose#13607)

**Full Changelog**: <docker/compose@v5.0.2...v5.1.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4zNS4xIiwidXBkYXRlZEluVmVyIjoiNDMuMzUuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
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