Skip to content

fix: update com.docker.compose.image label when recreating container#2183

Open
GiulioSavini wants to merge 1 commit intogetarcaneapp:mainfrom
GiulioSavini:fix/compose-image-label-after-update
Open

fix: update com.docker.compose.image label when recreating container#2183
GiulioSavini wants to merge 1 commit intogetarcaneapp:mainfrom
GiulioSavini:fix/compose-image-label-after-update

Conversation

@GiulioSavini
Copy link
Copy Markdown
Contributor

@GiulioSavini GiulioSavini commented Mar 29, 2026

Checklist

  • This PR is not opened from my fork's main branch

What This PR Fixes

When Arcane updates a container via the standalone path (i.e. when the compose project is not registered in the DB), it copies all labels from the old container verbatim — including com.docker.compose.image, which stores the old image ID.

After the update, running docker compose up -d sees that com.docker.compose.image doesn't match the current image ID and recreates the container, breaking compatibility with docker compose.

Fixes #1935

Root Cause

In updateContainer(), the container config (including all labels) is copied from the old container and only cfg.Image is updated to the new ref. The com.docker.compose.image label is left pointing to the old image ID.

Docker Compose uses this label to determine whether a container is up-to-date. A mismatch triggers an unnecessary recreate.

Changes Made

  • updater_service.go — After setting cfg.Image = newRef, inspect the newly pulled image and update com.docker.compose.image to the new image ID (only when the label already exists on the container). Falls back gracefully with a warning log if the image inspect fails.

Why This Is Safe

  • The label is only updated when it already exists (i.e. only for compose-managed containers)
  • The compose-aware update path (UpdateProjectServicesdocker compose up) is not affected
  • If ImageInspect fails for any reason, the old behavior is preserved (label unchanged) with a warning log
  • No changes to container creation, networking, or other config

Testing Done

  • go vet ./internal/services/ — no issues
  • go test ./internal/services/ -run TestUpdater — all tests pass

AI Tool Used (if applicable)

AI Tool: N/A
Assistance Level: N/A

Disclaimer Greptiles Reviews use AI, make sure to check over its work.

To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike

To have Greptile Re-Review the changes, mention greptileai.

Greptile Summary

This PR fixes a label-staleness bug in the standalone (non-compose-DB) container update path: after Arcane recreates a container with a new image, the com.docker.compose.image label was left pointing to the old image ID, causing docker compose up -d to trigger a redundant recreate. The fix inspects the newly-pulled image immediately after setting cfg.Image = newRef and updates the label to the fresh image ID — only when the label already exists on the container, and with a graceful fallback if the inspect fails.

Key changes

  • updater_service.go: After updating cfg.Image, calls dcli.ImageInspect to resolve the new image ID and writes it back to cfg.Labels[\"com.docker.compose.image\"]. The update is guarded by a nil-check on cfg.Labels and an existence-check for the label key, so containers that were never compose-managed are unaffected.
  • The change is isolated to the standalone update path (updateContainer); the compose-aware path (UpdateProjectServices) is unchanged.

Confidence Score: 5/5

Safe to merge — the fix is narrowly scoped, well-guarded, and does not affect the compose-aware update path.

The only finding is a pre-existing P2 naming-convention issue (updateContainer should be updateContainerInternal) that this PR touches but did not introduce. The logic itself is correct: it guards against a nil Labels map, only updates the key when it already exists, and falls back gracefully on inspect failure. No new security, correctness, or reliability concerns were introduced.

No files require special attention.

Important Files Changed

Filename Overview
backend/internal/services/updater_service.go Adds a guarded update of the com.docker.compose.image label inside updateContainer so that after a standalone image refresh the label reflects the new image ID, preventing Docker Compose from treating the container as stale. Logic is well-guarded (nil-check, existence-check, graceful fallback). Pre-existing naming-convention violation: function should be updateContainerInternal.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[updateContainer called] --> B[Stop old container]
    B --> C[Remove old container]
    C --> D["cfg := inspect.Config\ncfg.Image = newRef"]
    D --> E{cfg.Labels != nil?}
    E -- No --> H
    E -- Yes --> F{"com.docker.compose.image\nexists in labels?"}
    F -- No --> H
    F -- Yes --> G["dcli.ImageInspect(ctx, newRef)"]
    G -- success --> I["cfg.Labels[com.docker.compose.image] = imgInspect.ID"]
    G -- error --> J["slog.WarnContext: could not inspect new image"]
    I --> H
    J --> H[PrepareRecreateHostConfigForEngine]
    H --> K[ContainerCreate with updated cfg]
    K --> L[Start new container]
Loading

Comments Outside Diff (1)

  1. backend/internal/services/updater_service.go, line 748 (link)

    P2 updateContainer missing "Internal" suffix

    Per the project's naming convention, all unexported functions must carry an Internal suffix. updateContainer (and its two call sites at lines 640 and 1535) should be renamed to updateContainerInternal for consistency with the rest of the file (e.g. pruneImageIDsWithInUseSetInternal, collectUsedImagesFromContainersInternal, buildRunningImageIDSetInternal).

    Rule Used: What: All unexported functions must have the "Inte... (source)

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: backend/internal/services/updater_service.go
    Line: 748
    
    Comment:
    **`updateContainer` missing "Internal" suffix**
    
    Per the project's naming convention, all unexported functions must carry an `Internal` suffix. `updateContainer` (and its two call sites at lines 640 and 1535) should be renamed to `updateContainerInternal` for consistency with the rest of the file (e.g. `pruneImageIDsWithInUseSetInternal`, `collectUsedImagesFromContainersInternal`, `buildRunningImageIDSetInternal`).
    
    
    
    **Rule Used:** What: All unexported functions must have the "Inte... ([source](https://app.greptile.com/review/custom-context?memory=306fc233-4d2f-4ac4-bdf7-8059588e8a43))
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: backend/internal/services/updater_service.go
Line: 748

Comment:
**`updateContainer` missing "Internal" suffix**

Per the project's naming convention, all unexported functions must carry an `Internal` suffix. `updateContainer` (and its two call sites at lines 640 and 1535) should be renamed to `updateContainerInternal` for consistency with the rest of the file (e.g. `pruneImageIDsWithInUseSetInternal`, `collectUsedImagesFromContainersInternal`, `buildRunningImageIDSetInternal`).

```suggestion
func (s *UpdaterService) updateContainerInternal(ctx context.Context, cnt container.Summary, inspect container.InspectResponse, newRef string) error {
```

**Rule Used:** What: All unexported functions must have the "Inte... ([source](https://app.greptile.com/review/custom-context?memory=306fc233-4d2f-4ac4-bdf7-8059588e8a43))

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: update com.docker.compose.image lab..." | Re-trigger Greptile

Context used:

  • Rule used - What: All unexported functions must have the "Inte... (source)

When Arcane updates a container via the standalone path (not through
docker compose up), it copies all labels from the old container verbatim.
The com.docker.compose.image label retains the old image ID, causing
docker compose up -d to detect a mismatch and needlessly recreate the
container.

After setting the new image ref, inspect the pulled image and update
the compose label to match the new image ID.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐞 Bug: After arcane updates a container, docker compose does not detect the update and recreates it on running docker compose up -d

1 participant