Skip to content

fix(container): trust bind-mounted repos despite the VirtioFS uid flap - #177

Merged
radutopala merged 1 commit into
mainfrom
fix/git-safe-directory-in-agent-image
Aug 27, 2026
Merged

fix(container): trust bind-mounted repos despite the VirtioFS uid flap#177
radutopala merged 1 commit into
mainfrom
fix/git-safe-directory-in-agent-image

Conversation

@radutopala

Copy link
Copy Markdown
Owner

Problem

Docker Desktop's VirtioFS intermittently stats host bind mounts as uid 0 instead of the host uid. Git checks the worktree directory's owner, so on those calls it aborts:

fatal: detected dubious ownership in repository at '<project dir>'

Measured a 15/15 correlation inside an agent container — git fails on exactly the probes where the worktree dir stats as 0, succeeds when it stats as the host uid:

 1  worktree_uid=0    .git_uid=501  git=BLOCKED
 2  worktree_uid=501  .git_uid=501  git=OK
 3  worktree_uid=0    .git_uid=501  git=BLOCKED
 ...

Observed failure rates ran 33–58% depending on the window. It affects every VirtioFS bind mount; the ext4 named cache volumes never flap.

The flap hits the worktree dir far more often than .git, so probing .git alone shows a stable owner and the failures look like a ghost with no cause. Scheduled agent runs have been aborting over it — correctly refusing to make destructive decisions when git worktree list, git branch, and gh pr list can't be trusted to agree.

To be clear about what this is not: the entrypoint's chown loop never touches project directories. CHOWN_PATHS only covers the cache volumes (.npm, .local/share/uv, .cache, /go, .ollama, .claude.json), and those are ext4, not VirtioFS.

Fix

Set safe.directory = * at the system level in the agent image.

  • safe.directory is additive across config scopes, so a system entry survives the host ~/.gitconfig that gets mounted over the agent's global config.
  • /etc/gitconfig is container-local — nothing on the host is modified. Writing the user's ~/.gitconfig would not be acceptable, since it's a bind mount of their real file.
  • The trust check buys nothing in this container: single-tenant, already running with permissions skipped, and only the user's own directories are ever mounted.

Verification

  • Live flap window: 40/40 plain git calls failed; 40/40 with the system entry succeeded, over the same 40 probes where the uid read non-host every time.
  • Confirmed the RUN line writes and reads back on the real golang:1.27 base image.
  • make coverage-check — 100.0%
  • make lint-go — 0 issues

Migration

Ships a refreshContainerFiles migration so existing installs get the updated Dockerfile before their next image rebuild, and repins the digest guard added with the previous refresh — which is what caught the need for this migration in the first place.

Docker Desktop's VirtioFS intermittently stats host bind mounts as uid 0
instead of the host uid. Git checks the worktree directory's owner, so on
those calls it aborts with "detected dubious ownership" and the whole command
fails. Measured 15/15 correlation between a non-host uid on the worktree dir
and the git failure, at rates between 33% and 58% depending on the window.

The flap hits the worktree dir far more often than .git, so probing .git
alone shows a stable owner and the failures look like a ghost. Agents have
been aborting scheduled runs over it rather than act on unreliable reads.

Set safe.directory to * at the system level in the agent image.
safe.directory is additive across config scopes, so a system entry survives
the host ~/.gitconfig that gets mounted over the agent's global config, and
nothing on the host is modified. The trust check buys nothing in this
container anyway: single-tenant, permissions already skipped, and only the
user's own directories are ever mounted.

Verified in a live flap window: 40/40 plain git calls failed while 40/40 with
the system entry succeeded. Also confirmed the RUN line writes and reads back
on the real golang:1.27 base image.

Ships with a refreshContainerFiles migration so existing installs get the
updated Dockerfile before their next image rebuild, and repins the guard
digest added alongside the previous refresh.
@radutopala
radutopala enabled auto-merge (rebase) August 27, 2026 07:40
@radutopala
radutopala disabled auto-merge August 27, 2026 07:46
@radutopala
radutopala merged commit 56d2dde into main Aug 27, 2026
17 checks passed
@radutopala
radutopala deleted the fix/git-safe-directory-in-agent-image branch August 27, 2026 07:46
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.

1 participant