From 89e2cb58144d2e9ffc24a239e1b1ea03a5eff76a Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Sat, 5 Sep 2026 16:18:15 +0800 Subject: [PATCH] fix(docker): let the runtime account update its own global CLIs The four CLIs (claude, gemini, codex, opencode) are npm-installed globally as root during the image build, before the unprivileged runtime account exists. A session running as that account (e.g. a codex-mode terminal) then hits EACCES the moment it tries to update one in place, because npm renames the old package directory aside before installing the new one, which needs write access to the parent (/usr/local/lib/node_modules), not just the target package. Chown that tree plus /usr/local/bin's CLI symlinks to PUID:PGID in the same step that creates/renames the runtime account. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01R9ZSTEenc8soSu9bTi8Xru --- docker/server.Dockerfile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docker/server.Dockerfile b/docker/server.Dockerfile index bf71c621..c8a37475 100644 --- a/docker/server.Dockerfile +++ b/docker/server.Dockerfile @@ -93,6 +93,15 @@ RUN npm install --global \ # PGID match the host-owned application-data directory mounted by Compose. The # requested GID may not exist in the base image, and a host UID such as 1000 may # already belong to the baked `node` account, so handle both cases explicitly. +# +# The trailing chown hands the globally-installed CLIs to that same account. +# They were `npm install --global`-ed above while still root, so +# /usr/local/lib/node_modules (and the /usr/local/bin symlinks pointing into it) +# start out root-owned; a session running as the unprivileged runtime user then +# hits EACCES the moment it tries to self-update one in place (observed via +# Codex's own `npm install -g @openai/codex`, which renames the old package dir +# aside before installing the new one — a rename needs write access to the +# PARENT directory, not just the target, so this must chown the whole tree). RUN set -eux; \ case "${PUID}" in ''|*[!0-9]*) echo "PUID must be numeric" >&2; exit 1;; esac; \ case "${PGID}" in ''|*[!0-9]*) echo "PGID must be numeric" >&2; exit 1;; esac; \ @@ -120,7 +129,8 @@ RUN set -eux; \ --home-dir "/home/${CODEMAN_RUNTIME_USER}" \ --shell /bin/bash \ "${CODEMAN_RUNTIME_USER}"; \ - fi + fi; \ + chown -R "${PUID}:${PGID}" /usr/local/lib/node_modules /usr/local/bin WORKDIR /opt/codeman