Summary
When developers use docker exec against a running dev_containers instance (rootful, gosu path), HOME is inherited from the image's ENV HOME=... directive (/home/dev) rather than from the runtime user's actual home (/home/$(whoami)). This forces every developer to remember export HOME=/home/$(whoami) before any command that touches XDG paths (logs, state, config), defeating the dev_containers value proposition: builds should be reproducible without manual environmental fix-up.
This is a v1.0.x release blocker (adafmt v1.0.0 depends on dev_containers behaving correctly across organizations).
Owner-locked context (2026-05-07)
"We do not want devs to have to remember to do something manually. Defeats the purpose of the container."
— dev_containers is actively used by developers in other organizations. The container's value proposition is reproducibility across hosts AND a uniform developer experience without per-host manual workarounds.
What's broken
Symptom
docker exec -u mike -w /workspace/adafmt dev-container-ada-system-1 \
bash -lc 'echo $HOME; ./bin/adafmt check src/'
# Output:
# /home/dev <-- wrong; should be /home/mike
# Log: /home/dev/.local/state/adafmt/log/... <-- log path follows wrong HOME
# Text_Log open failed: Text_Log sink: open failed for path
# '/home/dev/.local/state/adafmt/log/...'
# (directory not writable or path invalid). <-- mike can't write to dev's home
Why
- The Dockerfile chain establishes the build-time identity as
${USERNAME} (default dev), with HOME=/home/dev baked into image ENV.
- The
entrypoint.sh adapts the runtime identity at container launch (rootful path: useradd matching host UID, then exec gosu "${HOST_USER}" "$@"). This sets HOME=${TARGET_HOME} for the CMD-spawned process.
docker exec does NOT go through the entrypoint. It spawns processes directly as the requested user via Docker's execution model, inheriting the image's ENV (still HOME=/home/dev).
- Login shells (
bash -l, zsh -l) inherit HOME from the docker-set environment, which trumps /etc/passwd. So even though useradd correctly recorded mike's home as /home/mike in /etc/passwd, the login shell still sees HOME=/home/dev and writes user state there.
Net effect
- Tools that use
~/.config, ~/.local/state, ~/.cache (XDG dirs) write to /home/dev/... paths but the runtime user mike (UID 501) doesn't own /home/dev (the dev user UID 1000 does, with chmod 750). Permission denied.
- Worked around in adafmt session 2026-05-07 with
export HOME=/home/$(whoami) per-invocation — fine as a tactical workaround but not a long-term contract.
Required fix
Make docker exec get the right HOME automatically, without per-invocation export.
Two approach options
(A) /etc/profile.d/ snippet (recommended)
Add a small, idempotent script that runs on every login shell to fix HOME based on whoami:
# /etc/profile.d/host-home.sh
# Auto-correct HOME for docker-exec-spawned login shells.
# The Dockerfile's ENV HOME=/home/dev is correct for the build-time
# user but wrong for the runtime host-UID-matched user.
expected_home="/home/$(whoami)"
if [ -d "$expected_home" ] && [ "$HOME" != "$expected_home" ]; then
export HOME="$expected_home"
fi
Created via Dockerfile.system RUN step; chmod 644. Runs from any login shell (bash -l, zsh -l, sh -l). Idempotent (no-op if HOME already correct).
(B) Remove ENV HOME=... from Dockerfile
Don't bake HOME into image ENV; let /etc/passwd lookup populate HOME for login shells. Simpler but might break other paths that rely on ${HOME} resolving at image-build time (e.g., ENV PATH=${HOME}/.local/bin:...).
Recommendation: (A). Less risk to existing path resolution.
Implementation scope
- Edit
Dockerfile.system (and Dockerfile for symmetry) to add the profile.d RUN step.
- Edit
entrypoint.sh only if there's a path where the entrypoint's own export is wrong. Likely no change needed — the entrypoint's HOME export remains correct for entrypoint-driven processes.
- No changes needed for
docker run (entrypoint path) — that path already works.
- Mostly affects the
docker exec path.
Verification
After image rebuild + redeploy, the following should "just work" without manual export:
docker exec -u $(whoami) -w /workspace/adafmt dev-container-ada-system-1 \
bash -lc 'echo $HOME; ./bin/adafmt check src/'
# Expected:
# /home/$(whoami)
# Log: /home/$(whoami)/.local/state/adafmt/log/...
# Files: ... processed, ... ok ...
Tests:
- Test-1:
docker exec -u <host_user> spawned bash login shell reports $HOME == /home/<host_user>.
- Test-2:
docker exec -u <host_user> spawned bash login shell can write to ~/.local/state/.
- Test-3: regression —
docker run (entrypoint path) still gets $HOME == /home/<host_user> (no break).
- Test-4:
docker exec as root (UID 0, kubectl-style runAsUser=0) doesn't break — root has its own /root home.
- Test-5: rootless runtime (entrypoint stays as UID 0) —
docker exec -u 0 and unsetenv'd HOME still resolves to TARGET_HOME via the existing entrypoint logic.
Out of scope
- macOS
Docker Desktop host file-system permissions on bind mounts (separate concern; not affected).
- Windows host docker exec (uses different path conventions; out of scope until Windows 11 platform expansion lands per adafmt#68).
References
- adafmt session 2026-05-07: discovered during
bin/adafmt check runs inside adast as mike; tactical workaround was per-invocation export HOME=/home/mike.
- entrypoint.sh lines 91-94 (HOST_USER / TARGET_HOME computation).
- entrypoint.sh lines 270-281 (rootful HOME export + gosu exec).
- Dockerfile.system existing RUN step pattern (
alr config --global --set toolchain.assistant false) — the new profile.d RUN goes near here.
Sequencing
- Targeted for the next dev_containers release (1.0.x or 1.1.0 — owner's call on version slot).
- Adafmt v1.0.0 release readiness depends on this landing.
- Pre-phase ask + GPT review for the profile.d snippet wording (cover edge cases in §Verification).
- Edit + commit + push dev_containers.
- Image rebuild + redeploy on each developer's host.
- Revert any tactical
export HOME=... workarounds in adafmt's docs / scripts (none committed today; just chat-level).
Summary
When developers use
docker execagainst a running dev_containers instance (rootful, gosu path),HOMEis inherited from the image'sENV HOME=...directive (/home/dev) rather than from the runtime user's actual home (/home/$(whoami)). This forces every developer to rememberexport HOME=/home/$(whoami)before any command that touches XDG paths (logs, state, config), defeating the dev_containers value proposition: builds should be reproducible without manual environmental fix-up.This is a v1.0.x release blocker (adafmt v1.0.0 depends on dev_containers behaving correctly across organizations).
Owner-locked context (2026-05-07)
What's broken
Symptom
Why
${USERNAME}(defaultdev), withHOME=/home/devbaked into image ENV.entrypoint.shadapts the runtime identity at container launch (rootful path:useraddmatching host UID, thenexec gosu "${HOST_USER}" "$@"). This setsHOME=${TARGET_HOME}for theCMD-spawned process.docker execdoes NOT go through the entrypoint. It spawns processes directly as the requested user via Docker's execution model, inheriting the image's ENV (stillHOME=/home/dev).bash -l,zsh -l) inheritHOMEfrom the docker-set environment, which trumps/etc/passwd. So even thoughuseraddcorrectly recorded mike's home as/home/mikein/etc/passwd, the login shell still seesHOME=/home/devand writes user state there.Net effect
~/.config,~/.local/state,~/.cache(XDG dirs) write to/home/dev/...paths but the runtime usermike(UID 501) doesn't own/home/dev(the dev user UID 1000 does, with chmod 750). Permission denied.export HOME=/home/$(whoami)per-invocation — fine as a tactical workaround but not a long-term contract.Required fix
Make
docker execget the right HOME automatically, without per-invocation export.Two approach options
(A) /etc/profile.d/ snippet (recommended)
Add a small, idempotent script that runs on every login shell to fix HOME based on
whoami:Created via
Dockerfile.systemRUN step; chmod 644. Runs from any login shell (bash -l, zsh -l, sh -l). Idempotent (no-op if HOME already correct).(B) Remove
ENV HOME=...from DockerfileDon't bake HOME into image ENV; let
/etc/passwdlookup populate HOME for login shells. Simpler but might break other paths that rely on${HOME}resolving at image-build time (e.g.,ENV PATH=${HOME}/.local/bin:...).Recommendation: (A). Less risk to existing path resolution.
Implementation scope
Dockerfile.system(andDockerfilefor symmetry) to add the profile.d RUN step.entrypoint.shonly if there's a path where the entrypoint's own export is wrong. Likely no change needed — the entrypoint's HOME export remains correct for entrypoint-driven processes.docker run(entrypoint path) — that path already works.docker execpath.Verification
After image rebuild + redeploy, the following should "just work" without manual export:
Tests:
docker exec -u <host_user>spawned bash login shell reports$HOME == /home/<host_user>.docker exec -u <host_user>spawned bash login shell can write to~/.local/state/.docker run(entrypoint path) still gets$HOME == /home/<host_user>(no break).docker execas root (UID 0, kubectl-style runAsUser=0) doesn't break — root has its own /root home.docker exec -u 0and unsetenv'd HOME still resolves to TARGET_HOME via the existing entrypoint logic.Out of scope
Docker Desktophost file-system permissions on bind mounts (separate concern; not affected).References
bin/adafmt checkruns insideadastasmike; tactical workaround was per-invocationexport HOME=/home/mike.alr config --global --set toolchain.assistant false) — the new profile.d RUN goes near here.Sequencing
export HOME=...workarounds in adafmt's docs / scripts (none committed today; just chat-level).