Skip to content

fix(docker): mkdir HERMES_HOME as root in stage2 before chown / privilege drop (#18488)#33078

Merged
benbarclay merged 1 commit into
mainfrom
salvage-18488-mkdir-hermes-home
May 27, 2026
Merged

fix(docker): mkdir HERMES_HOME as root in stage2 before chown / privilege drop (#18488)#33078
benbarclay merged 1 commit into
mainfrom
salvage-18488-mkdir-hermes-home

Conversation

@benbarclay

Copy link
Copy Markdown
Collaborator

Salvages #18488 (@wpengpeng168) — fixes #18482.

Problem

When HERMES_HOME points at a custom path whose parent directories only root can create (e.g. HERMES_HOME=/home/hermes/.hermes in a Compose file, or any path under a fresh root not pre-populated by the image), stage2-hook.sh fails on first boot:

[stage2] Warning: chown /custom/hermes-home/.hermes failed (rootless container?) — continuing
mkdir: cannot create directory '/custom': Permission denied
mkdir: cannot create directory '/custom': Permission denied
... (one per s6-setuidgid hermes mkdir invocation, 10 total)
cont-init: info: /etc/cont-init.d/01-hermes-setup exited 1

The mkdirs fail because s6-setuidgid hermes drops to UID 10000 before invoking mkdir -p, and the runtime user has no permission to create root-owned ancestor directories like /home/ or /custom/. 02-reconcile-profiles then crashes with three FileNotFoundErrors, .install_method never lands, and the container limps on in a half-initialized state.

This was originally reported as #18482 and #18488 attempted to fix the pre-s6 docker/entrypoint.sh. The shim is deprecated now but the underlying bug moved into docker/stage2-hook.sh during the s6-overlay rework.

Fix

Bootstrap HERMES_HOME with mkdir -p while still root, before the ownership normalization and the s6-setuidgid hermes mkdir -p block. Idempotent on the default /opt/data path (directory already exists from the Dockerfile's RUN mkdir -p) and on any subsequent container restart.

# --- Bootstrap HERMES_HOME as root ---
# Create the directory (and any missing parents) while we still have root
# privileges so the chown checks below see real metadata and the later
# `s6-setuidgid hermes mkdir -p` block doesn't EACCES on root-owned
# ancestors.
mkdir -p "$HERMES_HOME"

Validation

Bug reproduction (baseline vs salvage)

Built an isolated E2E that runs docker run -e HERMES_HOME=/custom/hermes-home/.hermes <tag> --version (no volume — exercises the in-container fs) and inspects the stage2 log + post-run filesystem state. Compares baseline (origin/main) against this salvage:

Check Baseline Salvage
01-hermes-setup exited 0 ✗ exit 1
No mkdir EACCES errors ✗ 10 failures
No FileNotFoundError in 02-reconcile-profiles ✗ 3 tracebacks
stage2 chown path triggered
HERMES_HOME directory created on disk partial
stage2 mkdir block populated cron/, logs/, ...
.install_method stamp written
Default HERMES_HOME=/opt/data still boots cleanly
Total 3 / 8 8 / 8

Regression battery (all on the salvage image)

Suite Result
Image smoke (--version, hermes_cli, TUI launcher, lazy_deps, gcc) 6/6
Chown E2E (#19788 regression check from salvage 2) 6/6
TUI UID-remap E2E (#28851 regression check from salvage 3) 4/4
Node version E2E (#4977 regression check) 8/8
Total 24 / 24

Authorship

Original change by @wpengpeng168 in #18488 — they identified and patched the same root cause in the pre-s6 docker/entrypoint.sh. Retargeted to docker/stage2-hook.sh and adjusted the placement to fit the s6-overlay cont-init flow. Preserved attribution via Co-authored-by:.

Closes #18488.
Fixes #18482.

…lege drop (#18488)

When HERMES_HOME points at a custom path whose parent directories
only root can create (e.g. HERMES_HOME=/home/hermes/.hermes in a
Compose file, or any path under a fresh / not pre-populated by the
image), stage2-hook.sh fails on first boot:

  [stage2] Warning: chown failed (rootless container?) - continuing
  mkdir: cannot create directory '/custom': Permission denied
  mkdir: cannot create directory '/custom': Permission denied
  ... (one per s6-setuidgid hermes mkdir invocation)
  cont-init: info: /etc/cont-init.d/01-hermes-setup exited 1

The mkdirs fail because s6-setuidgid drops to hermes (UID 10000)
before invoking mkdir -p, and the runtime user has no permission to
create root-owned ancestor directories. 02-reconcile-profiles then
crashes with FileNotFoundError, .install_method never lands, and
the container limps on in a half-initialized state.

Bootstrap HERMES_HOME with mkdir -p while still root, before the
ownership normalization. Idempotent on the default /opt/data path
(directory already exists from the Dockerfile RUN mkdir -p) and on
any subsequent restart. (#18482)

Retargeted from the original PR's docker/entrypoint.sh (now a
deprecated shim) to docker/stage2-hook.sh where the related chown
logic moved during the s6-overlay rework.

Co-authored-by: wpengpeng168 <133926080+wpengpeng168@users.noreply.github.com>
@benbarclay benbarclay added the area/docker Docker image, Compose, packaging label May 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: salvage-18488-mkdir-hermes-home vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9400 on HEAD, 9400 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4973 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@benbarclay benbarclay merged commit fb298a9 into main May 27, 2026
22 of 23 checks passed
@benbarclay benbarclay deleted the salvage-18488-mkdir-hermes-home branch May 27, 2026 07:16
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists labels May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docker Docker image, Compose, packaging P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Cannot create home directory: Permission denied

2 participants