4.8 KiB
Fix slow/failing chown -R in entrypoint.sh on Mac ARM bind mounts
Context
On macOS (Apple Silicon), Docker Desktop bind-mounts host project folders (e.g.
/home/aleleba/projects/telus/telus-board-report) into the container over a virtualized
filesystem (virtiofs/gRPC-FUSE) that does not support changing file ownership from inside
the container. entrypoint.sh:79 runs:
sudo chown -R ${HOME_USER} /home/${HOME_USER}
This recurses into those bind-mounted directories too, including huge node_modules
trees, and every single file inside them fails with Operation not permitted. Two real
problems come from this, not just one:
- Speed —
chown -Rwalks every file individually; over tens of thousands ofnode_modules/.venventries this is very slow. - Correctness (bigger issue found during investigation) — the script starts with
set -eu.chown -Rstill returns a non-zero exit status once it finishes (even though it keeps going and reports every failure), and this line is not guarded by|| trueor anif. Confirmed locally: a failingchownunderset -eaborts the script immediately at that line. That means once this line fails, everything after it in entrypoint.sh never runs — no.sshpermission fixup, no custom-scripts, no extension install, and critically the finalcode tunnel ...command that actually starts the VS Code tunnel. So this isn't just "slow," it can silently prevent the container from ever reaching the tunnel start step on a run where a permission error occurs.
The user has confirmed ownership doesn't actually need to change on these mounted
folders — the Dockerfile already does chmod -R a+rwX /home at build time (Dockerfile:41),
and the host-mounted files are already world-writable (777), so aleleba can read/write
them regardless of chown succeeding.
Fix
Edit entrypoint.sh:79, replacing the whole-home recursive chown with a find -xdev
based version that:
- Stays on the container's own filesystem (
-xdev) so it never descends into separately-mounted bind volumes like/home/${HOME_USER}/projects/...— this is what fixes the slowness, without hardcoding folder names likenode_modules/.venv(it works generically for any bind-mounted subtree, not just those two). - Never aborts the script even if an unexpected chown failure occurs elsewhere, by
appending
|| true(this is the correctness fix — restores the guarantee that the rest of entrypoint.sh, including the VS Code tunnel startup, always runs).
sudo find "/home/${HOME_USER}" -xdev -exec chown "${HOME_USER}" {} + 2>/dev/null || true
This is a single-line change to entrypoint.sh; no other recursive chown/chmod calls in
the script (lines 72, 132, 137, 142) are touched, since those aren't the ones hitting
bind-mounted project directories.
This is not a Mac-only fix. -xdev skips any directory that is a separate mount
point (different device id) from /home/${HOME_USER} itself — that's true for bind
mounts on Linux/Synology just as much as on macOS. The difference is only in symptom:
- On macOS (virtiofs/gRPC-FUSE),
chownon the bind-mounted files fails outright (Operation not permitted), which — combined with theset -ebug above — can abort the whole entrypoint before the tunnel starts. - On Linux, the same
chown -Rwalk intonode_modules/.venvsucceeds file-by-file (root can chown across a Linux bind mount), so there's no error output, but it still pays the same per-file syscall cost — this is almost certainly why the user's other, working Linux/Synology environment is also slow to start, just silently.
Because the fix skips crossing the mount boundary rather than special-casing specific
folder names or the host OS, one change fixes both the correctness bug (Mac) and the
startup latency (all environments) with no behavior change for anything that lives on
the container's own filesystem (.ssh, dotfiles, .vscode*, etc. are chowned exactly as
before).
Verification
- Read the diff to confirm only line 79 changed and the semantics (still chowning to
HOME_USER, still running as root viasudo) are preserved. - Locally simulate the
set -eabort scenario (same repro used during investigation: achown -Rhitting a permission-denied/missing path underset -eukills the script) and confirm the newfind -xdev ... || trueline does not abort aset -euscript even when it encounters a permission error. - Rebuild the image (
docker build .) to confirm the Dockerfile/entrypoint still builds cleanly. - If possible, run the container with a bind-mounted
node_modules-heavy directory (mirroring the user's Mac setup) and confirm: noOperation not permittedspam, the entrypoint completes quickly, and thecode tunnelcommand still launches at the end of the log.