Skip to content

fix(docker): HERMES_UID and HERMES_GID usage in container + feature(docker): add new SYNC_CODE_DIR environment variable#12623

Closed
mmartial wants to merge 2 commits into
NousResearch:mainfrom
mmartial:main
Closed

fix(docker): HERMES_UID and HERMES_GID usage in container + feature(docker): add new SYNC_CODE_DIR environment variable#12623
mmartial wants to merge 2 commits into
NousResearch:mainfrom
mmartial:main

Conversation

@mmartial

@mmartial mmartial commented Apr 19, 2026

Copy link
Copy Markdown

What does this PR do?

The entrypoint contains runtime ownership/remap logic that only works correctly when the container starts as root. After the image build switched to running as hermes, that logic no longer had the privileges needed to honor HERMES_UID / HERMES_GID.

Separately, some downstream/containerized workflows need access to the exact Hermes source code shipped in the image so they can install it into their own virtualenv or consume files like requirements.txt from a mounted path.

This PR makes two Docker-focused changes:

  1. Restores the intended HERMES_UID / HERMES_GID behavior by switching the image back to root before the runtime entrypoint executes.
  2. Adds an optional SYNC_CODE_DIR environment variable that rsyncs the installed Hermes source tree to a writable external directory at container startup (to allow third party solutions to add hermes agent to their venv; example: Hermes WebUI)

Related Issue

I did not see an existing issue, and created one #12696

I discovered the issue while trying to use HERMES_UID and HERMES_GID and found that they were not being respected (the intent was to provide an Unraid template for Hermes Agent, and the Hermes WebUI project; the second one would need the new feature to function)

The root cause is the USER hermes usage in the Dockerfile while the entrypoint.sh checks against id = 0 (ie the root user)

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)

Changes Made

  • Dockerfile:

    • Installs rsync
    • Switches back to USER root after dependency installation so the entrypoint.sh can perform UID/GID and ownership setup correctly at runtime
  • docker/entrypoint.sh

    • Adds optional SYNC_CODE_DIR support. If SYNC_CODE_DIR points to an existing writable directory, the entrypoint copies /opt/hermes into it with rsync (excludes runtime/build noise such as .venv, .git, __pycache__, *.pyc, *.pyo, and .playwright + uses --delete so the synced directory stays aligned with the source tree across restarts). Fails open: if the sync target is missing or not writable, container startup continues normally

Behavior: Default behavior is unchanged when SYNC_CODE_DIR is unset. The sync is best-effort and does not block container startup. This is mainly useful for bind-mounted development workflows and derived images that want to install Hermes into a separate venv.

How to Test

  1. docker build -f Dockerfile --tag ha:test .
  2. mkdir -p ../test/{data,src}
  3. docker run --rm -it -e HERMES_UID=$(id -u) -e HERMES_GID=$(id -g) -v $(pwd)/../test/data:/test/data -e HERMES_HOME=/test/data -v $(pwd)/../test/src:/test/src -e SYNC_CODE_DIR=/test/src ha:test

Result:

Changing hermes UID to 1000
Changing hermes GID to 1000
Dropping root privileges
Running as 1000:1000 (hermes:hermes)
SYNC_CODE_DIR is set to /test/src, copying code there
Code copied to SYNC_CODE_DIR successfully (...
Syncing bundled skills into ~/.hermes/skills/ ...
  + dogfood
[...]
Done: 77 new, 0 updated, 0 unchanged. 77 total bundled.
[... followed by Hermes TUI access]

Local folders:

  • ../test/data contains the hermes runtime (including config.yaml, ...) owned by user matching HERMES_UID and HERMES_GID
  • ../test/src contains the content of /opt/hermes (without the .venv and other dropped files/folders) to support usage with 3rd party extensions more easily

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
    • only fix container build and usage, no hermes behavior
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
    • described in how to test, this is not a recursion issue, same comment as above re no change to hermes behavior
  • I've tested on my platform: Ubuntu 24.04 w/ Docker

Documentation & Housekeeping

  • [N/A] I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • [N/A] I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • [N/A] I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • [N/A] I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • [N/A] I've updated tool descriptions/schemas if I changed tool behavior — or N/A

…cted + Feature: Add rsync for SYNC_CODE_DIR usage
… (using rsync) of the hermes agent source code in an external (mounted) location so that 3rd party tools can have access to it to install the version running in the container in their own venv (or use the requirements.txt for their installation). If the variable is not present, nothing is different from the regular usage
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists area/docker Docker image, Compose, packaging labels Apr 23, 2026
@mmartial

Copy link
Copy Markdown
Author

Closing since #12696 and nesquena/hermes-webui#1046

@mmartial mmartial closed this Apr 26, 2026
@mmartial

Copy link
Copy Markdown
Author

I would reopen the following PR based on mmartial/unraid-templates#2 (comment)

@mmartial

Copy link
Copy Markdown
Author

I have recently added Hermes Agent to Unraid.

To share content across all 3 Docker images, we must use a named Docker volume.
With a named volume, when you start a container and mount a new, empty named volume to a directory that already contains data within the container image, Docker automatically copies the existing files from the image into the volume.
This "auto-copy" feature does not work with bind mounts (linking a specific folder on your host: if you bind mount an empty host folder to /app/data, the container's folder will appear empty).
If the volume already contains data (e.g., from a previous run, an image update, etc), Docker will not overwrite it with the image's content; it will simply mount what is already in the volume. This is important for Hermes in particular, as it is the end-user's responsibility to docker volume rm hermes_shared_volume to get the content to update itself or use hermes update or the WebUI to update it.

This addition would allow a bind mount to support this feature.

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.

2 participants