Skip to content

fix(docker): make supervisord config portable across rootful + rootless keep-id#593

Merged
pancacake merged 1 commit into
HKUDS:devfrom
enihcam:fix/gosu-nonroot-skip
Jun 27, 2026
Merged

fix(docker): make supervisord config portable across rootful + rootless keep-id#593
pancacake merged 1 commit into
HKUDS:devfrom
enihcam:fix/gosu-nonroot-skip

Conversation

@enihcam

@enihcam enihcam commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Removes user=root from the [supervisord] section in both production and development supervisord configs. Supervisord now inherits PID 1's UID (root under rootful daemons; UID 1000 under rootless podman + userns_mode: keep-id) instead of forcing a privilege drop.

Bug

Under rootless podman + userns_mode: keep-id, container PID 1 is the host user (UID 1000). The previous user=root line in the [supervisord] section makes supervisord exit at startup with:

Error: Can't drop privilege as nonroot user
For help, use /usr/bin/supervisord -h

Source: supervisord's options.py refuses to drop privileges when not running as root (if self.user and not os.geteuid() == 0: self.usage(...)). Rootful daemons worked because PID 1 is root with CAP_SETUID; rootless + keep-id has neither.

Fix

Omit user= from [supervisord]. Supervisord runs as PID 1's UID. Children still drop to UID 1000 via the existing per-program user=deeptutor:

  • Rootful: PID 1 = root → supervisord = root; setuid(1000) for children works.
  • Rootless keep-id: PID 1 = UID 1000 → supervisord = UID 1000; setuid(1000) for children is a no-op.

/dev/fd/1,2 ownership matches PID 1's UID in both runtimes, so the supervisord stdout/stderr writes work without an explicit setuid dance.

Diff

-    user=root

(removed from both production and development [supervisord] blocks, plus an entrypoint comment rewrite explaining the new design)

1 file changed, 17 insertions(+), 13 deletions(-)

Test plan

  • Heredoc body has consistent no-indent format
  • Both [supervisord] sections confirmed without user=root
  • Entrypoint comment explains the new design rationale
  • podman build -t deeptutor:pr-test -f Dockerfile --target production . succeeds
  • podman compose -f compose.yaml up -d brings both services up healthy
  • End-to-end test of rootless + keep-id + host-mode (the bug-triggering combo) — supervisord starts, backend + frontend reach RUNNING, no CRIT
  • End-to-end test of rootful Docker (regression check) — same behavior as before this PR

Related

Discovered while

Trying to run the user's host-mode + BACKEND_HOST=127.0.0.1 + FRONTEND_HOST=127.0.0.1 setup against ghcr.io/hkuds/deeptutor:latest. The image started, the entrypoint env-setup lines printed (📌 API Base URL, 📌 Auth enabled, …), then died at the supervisord gate with the message above. Diagnosed by reading the supervisord config heredoc, the entrypoint heredoc, and the supervisord options.py source.

…ss keep-id

Removes `user=root` from the [supervisord] section in both production and
development supervisord configs, and rewrites the entrypoint comment to
explain why.

Background: under rootless podman + userns_mode: keep-id, container PID 1
runs as the host user (UID 1000) instead of root. The previous design
explicitly forced supervisord to drop privileges to root (`user=root`),
which works under rootful daemons (PID 1 is root, has CAP_SETUID) but
fails under rootless + keep-id — supervisord sees `user=root` in its
config, tries to setuid(0), lacks CAP_SETUID, and exits with
"Error: Can't drop privilege as nonroot user" (per supervisord
options.py: refuses to drop privileges when not running as root).

Fix: omit `user=` from the [supervisord] section so supervisord inherits
PID 1's UID (root in rootful; UID 1000 in rootless + keep-id). The
backend and frontend programs still drop to the unprivileged deeptutor
user via per-program `user=deeptutor` — setuid(1000) works because
either PID 1 is root (rootful) or PID 1 is already UID 1000 (rootless
keep-id, where the setuid is a no-op). /dev/fd/1,2 ownership matches
PID 1's UID in both runtimes, so supervisord's stdout/stderr writes
succeed without an explicit setuid dance.

Verified locally: clean heredoc indentation; supervisord config parses;
both [supervisord] sections confirmed without `user=root`.

Closes the rootless-podman + read-only-rootfs follow-up to PR HKUDS#577
(reported by the user after HKUDS#577 + HKUDS#586 merged).
@enihcam

enihcam commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@pancacake ping...

@pancacake
pancacake merged commit c2c5d7c into HKUDS:dev Jun 27, 2026
vaskoyudha added a commit to vaskoyudha/deeptutor-for-programmer-fork that referenced this pull request Jul 25, 2026
fix(docker): make supervisord config portable across rootful + rootless keep-id
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.

2 participants