Skip to content

fix(docker): run as non-root user, use virtualenv (salvage #5811) - #8226

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-3f0b13fc
Apr 12, 2026
Merged

fix(docker): run as non-root user, use virtualenv (salvage #5811)#8226
teknium1 merged 2 commits into
mainfrom
hermes/hermes-3f0b13fc

Conversation

@teknium1

@teknium1 teknium1 commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Salvage of #5811 by @m0n5t3r, stripped to hardening-only changes:

  • Non-root execution: gosu drops privileges from root to hermes user (UID 10000). Supports HERMES_UID/HERMES_GID env vars for host mount permission matching.
  • Virtualenv: Uses uv venv instead of pip install --break-system-packages. Entrypoint activates the venv before running hermes.
  • Pinned multi-stage builds: uv and gosu images pinned with SHA256 digests.
  • Playwright fix: PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright keeps the build-time chromium install outside the /opt/data volume mount, avoiding the per-startup re-download from the original PR.

What was stripped (deferred to follow-up)

  • Matrix deps (.[matrix] install)
  • Chromium browser deps, xvfb, fonts
  • git system package
  • Moving playwright install to entrypoint
  • Slim base image (keeping debian:13.4 full for compatibility)

What was preserved from current main

Related

Test plan

docker build -t hermes-test .
docker run --rm hermes-test id    # should show hermes user, not root
docker run --rm -e HERMES_UID=1000 hermes-test id   # should show UID 1000

m0n5t3r and others added 2 commits April 12, 2026 00:26
- Add gosu for runtime privilege dropping from root to hermes user
- Support HERMES_UID/HERMES_GID env vars for host mount permission matching
- Switch to debian:13.4-slim base image
- Use uv venv instead of pip install --break-system-packages
- Pin uv and gosu multi-stage images with SHA256 digests
- Set PLAYWRIGHT_BROWSERS_PATH to /opt/hermes/.playwright so build-time
  chromium install survives the /opt/data volume mount
- Keep procps for container debugging

Based on work by m0n5t3r in PR #5811. Stripped to hardening-only
changes (non-root, virtualenv, slim base); matrix deps, fonts, xvfb,
and entrypoint playwright download deferred to follow-up.
The slim image drops packages that may be needed at runtime.
Keep the full Debian base for compatibility.
@teknium1
teknium1 merged commit 8b9d22a into main Apr 12, 2026
2 of 4 checks passed
@teknium1
teknium1 deleted the hermes/hermes-3f0b13fc branch April 12, 2026 07:53
@m0n5t3r

m0n5t3r commented Apr 12, 2026

Copy link
Copy Markdown
Contributor
61.36 FFmpeg (playwright ffmpeg v1011) downloaded to /opt/hermes/.playwright/ffmpeg-1011
146.6 npm ERR! code 254
146.6 npm ERR! git dep preparation failed
146.6 npm ERR! command /usr/bin/node /usr/share/nodejs/npm/bin/npm-cli.js install --force --cache=/root/.npm --prefer-offline=true --prefer-online=false --offline=false --no-progress --no-save --no-audit --include=dev --include=peer --include=optional --no-package-lock-only --no-dry-run
146.6 npm ERR! npm WARN using --force Recommended protections disabled.
146.6 npm ERR! npm ERR! code ENOENT
146.6 npm ERR! npm ERR! syscall spawn git
146.6 npm ERR! npm ERR! path git
146.6 npm ERR! npm ERR! errno -2
146.6 npm ERR! npm ERR! enoent An unknown git error occurred
146.6 npm ERR! npm ERR! enoent This is related to npm not being able to find a file.
146.6 npm ERR! npm ERR! enoent 
146.6 npm ERR! 
146.6 npm ERR! npm ERR! A complete log of this run can be found in:
146.6 npm ERR! npm ERR!     /root/.npm/_logs/2026-04-12T11_19_31_897Z-debug-0.log
146.6 
146.6 npm ERR! A complete log of this run can be found in:
146.6 npm ERR!     /root/.npm/_logs/2026-04-12T11_19_30_403Z-debug-0.log

turns out git was needed after all; the docker build workflow seem to be failing in the same place, but I can't see the logs: https://github.com/NousResearch/hermes-agent/actions/runs/24305454757

@UmbraAtrox

Copy link
Copy Markdown

Sorry to bother but how do i give docker perms back to hermes when it's running as non-root?

Previously up to 0.8 it was root and dockering fine, now it's default 10000:10000 and permission denied.
Do i run it as 1000:docker? 0:0? group_add gets ignored/dropped with envs; and user: "1000:1000", group_add: - 987 instead of envs just bombs the container..
Can we have a HERMES_EXTRA_GROUPS ?

maybe relevant: #10096

(and Why is everything in /opt/hermes owned by default 10000:10000 when HERMES_UID/HERMES_GID are changed to 1000:1000 rather than either root or the env ids that are set?)

@m0n5t3r

m0n5t3r commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

I'll see in the morning and report back; in principle group_add won't do much because it applies to the USER set in the dockerfile, and here we launch the entrypoint as root, alter the hermes UID / GID, fix permissions (but we only look at the main directory ownership to avoid doing a chown -R on every container restart) and then drop privileges with gosu

I think setting HERMES_GID to the docker GID should work, but I have to check.

@m0n5t3r

m0n5t3r commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

just tested, running with HERMES_GID=984 (my docker GID), gosu will assume the user group as well:

root@9b63752cf9b7:/opt/hermes# gosu hermes id
uid=10000(hermes) gid=984(hermes) groups=984(hermes)

so it should be able to talk to the docker socket for dind purposes

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.

3 participants