Bug Description
In Docker deployments, Hermes normally starts correctly: the entrypoint runs as root, fixes ownership, and then drops privileges to the hermes user before running hermes gateway run.
The problem occurs when users enter the running container as root via docker exec and manually run hermes. This bypasses the entrypoint privilege-dropping logic, so the CLI runs as root and may create or modify $HERMES_HOME/config.yaml and other state files as root-owned.
For example, the following screenshot shows Hermes running as root even though HERMES_UID and HERMES_GID are set to 10000:
This demonstrates that:
- The CLI is executing as
root
- The environment variables (
HERMES_UID, HERMES_GID) are present but not enforced
- No privilege drop occurs in this execution path
Later, when the gateway runs normally as the hermes user, it can no longer access those files, causing permission errors.
The issue is not that the official gateway startup fails to drop privileges, but that interactive root execution is not guarded against and can silently poison the file ownership under $HERMES_HOME.
This may be related to previous permission-related reports such as #15865, where the reported symptom was:
chown: changing ownership of '/opt/data/config.yaml': Operation not permitted
PR #16096 fixed one specific entrypoint-side issue by moving the config.yaml chown/chmod handling into the root section before the gosu privilege drop, and by guarding the chown failure path.
However, the case described here is a different execution path: the user manually enters the running container as root and launches hermes, bypassing the entrypoint entirely. In that path, the fix from #16096 may repair config.yaml in some cases after gateway restart, but other files touched by the root-launched CLI or agent tools may still become root-owned.
This is also related in spirit to #4426 / #7357, where subprocess/tool environment isolation was improved by injecting a persistent HOME under $HERMES_HOME. That fix helps subprocesses write tool configuration into the persistent volume, but it does not prevent the main Hermes CLI process itself from being launched as root inside the container.
Therefore, the remaining issue is broader than config.yaml: any file under $HERMES_HOME created or modified by a root-launched Hermes CLI session may later become inaccessible to the normal gateway process running as the hermes user.
In later testing, restarting the gateway appeared to repair the ownership issue for config.yaml in some cases. However, permission errors may still occur for other files under $HERMES_HOME that were previously created or modified by the root-launched CLI or agent tools. For example, files related to history, memory, cache, logs, or state may also become root-owned and later fail to be read by the normal gateway process running as the hermes user.
Therefore, config.yaml is only one concrete example of the issue. The broader problem is that any file touched by a root-launched Hermes CLI session can become inaccessible to the normal non-root runtime.
Steps to Reproduce
-
Build and start Hermes in a Docker dev environment:
docker compose -p hermes-agent-dev up -d --build
-
Enter the running container as root:
docker exec -it hermes-agent-dev bash
-
Launch the Hermes from this root shell:
hermes setup
-
Configure Hermes in the root-launched setup flow and trigger the config backup/update path.
-
Verify file ownership:
ls -l config.yaml
The file is now owned by root.
-
Launch hermes:
hermes
-
Observe permission-related errors when the gateway runs as the hermes user.
chown: changing ownership of '/opt/data/config.yaml': Operation not permitted
Expected Behavior
When Hermes CLI is launched inside a Docker container, it should not execute any agent/tool command as root.
If the CLI is started from a root shell inside the container, Hermes should either:
- drop privileges to the
hermes user before running any agent/tool command, or
- refuse to continue and instruct the user to run Hermes as the
hermes user.
After restarting the gateway, it should continue to run normally as the hermes user without failing due to root-owned files under $HERMES_HOME.
Actual Behavior
After restarting the gateway, the service may fail with file permission / ownership errors because files under $HERMES_HOME have become root-owned.
config.yaml is one concrete example, but the same issue can also affect logs, cache files, state databases, or any other files created/modified by the root-launched CLI or agent tools.
Example error:
chown: changing ownership of '/opt/data/config.yaml': Operation not permitted
### Affected Component
Gateway (Telegram/Discord/Slack/WhatsApp), CLI (interactive chat), Configuration (config.yaml, .env, hermes setup)
### Messaging Platform (if gateway-related)
N/A (CLI only)
### Debug Report
```shell
--- hermes dump ---
version: 0.11.0 (2026.4.23) [(unknown)]
os: Linux 6.6.87.2-microsoft-standard-WSL2 x86_64
python: 3.13.5
openai_sdk: 2.32.0
profile: default
hermes_home: /opt/data
model: deepseek-v4-flash
provider: deepseek
terminal: local
api_keys:
openrouter not set
openai not set
anthropic not set
anthropic_token not set
nous not set
google/gemini not set
gemini not set
glm/zai not set
zai not set
kimi not set
minimax not set
deepseek set
dashscope not set
huggingface not set
nvidia not set
ai_gateway not set
opencode_zen not set
opencode_go not set
kilocode not set
firecrawl not set
tavily not set
browserbase not set
fal not set
elevenlabs not set
github not set
features:
toolsets: hermes-cli
mcp_servers: 0
memory_provider: built-in
gateway: running (docker (foreground), pid 7)
platforms: none
cron_jobs: 0
skills: 81
config_overrides:
display.streaming: True
--- end dump ---
--- agent.log (last 200 lines) ---
in "/opt/data/config.yaml", line 400, column 1
2026-04-27 09:04:42,447 INFO run_agent: Loaded environment variables from /opt/data/.env
2026-04-27 09:04:59,887 INFO hermes_cli.plugins: Plugin 'openai' registered image_gen provider: openai
2026-04-27 09:04:59,888 INFO hermes_cli.plugins: Plugin 'openai-codex' registered image_gen provider: openai-codex
2026-04-27 09:04:59,919 INFO hermes_cli.plugins: Plugin 'xai' registered image_gen provider: xai
2026-04-27 09:04:59,956 INFO hermes_cli.plugins: Plugin discovery complete: 5 found, 4 enabled
2026-04-27 09:05:00,588 INFO run_agent: Loaded environment variables from /opt/data/.env
2026-04-27 09:05:01,337 INFO agent.auxiliary_client: Vision auto-detect: using main provider deepseek (deepseek-v4-flash)
2026-04-27 09:05:02,221 WARNING gateway.config: Failed to process config.yaml — falling back to .env / gateway.json values. Check /opt/data/config.yaml for syntax errors. Error: [Errno 13] Permission denied: '/opt/data/config.yaml'
2026-04-27 09:05:02,268 WARNING gateway.config: Failed to process config.yaml — falling back to .env / gateway.json values. Check /opt/data/config.yaml for syntax errors. Error: [Errno 13] Permission denied: '/opt/data/config.yaml'
2026-04-27 09:05:03,042 INFO agent.auxiliary_client: Vision auto-detect: using main provider deepseek (deepseek-v4-flash)
2026-04-27 09:05:03,274 INFO agent.auxiliary_client: Vision auto-detect: using main provider deepseek (deepseek-v4-flash)
2026-04-27 09:05:06,946 INFO agent.auxiliary_client: Vision auto-detect: using main provider deepseek (deepseek-v4-flash)
2026-04-27 09:05:08,270 INFO agent.auxiliary_client: Auxiliary auto-detect: using main provider deepseek (deepseek-v4-flash)
2026-04-27 09:05:10,413 INFO agent.auxiliary_client: Auxiliary auto-detect: using main provider deepseek (deepseek-v4-flash)
2026-04-27 09:05:10,413 INFO agent.auxiliary_client: Auxiliary title_generation: using auto (deepseek-v4-flash) at https://api.deepseek.com
2026-04-27 09:06:02,203 WARNING gateway.config: Failed to process config.yaml — falling back to .env / gateway.json values. Check /opt/data/config.yaml for syntax errors. Error: [Errno 13] Permission denied: '/opt/data/config.yaml'
2026-04-27 09:06:02,244 WARNING gateway.config: Failed to process config.yaml — falling back to .env / gateway.json values. Check /opt/data/config.yaml for syntax errors. Error: [Errno 13] Permission denied: '/opt/data/config.yaml'
2026-04-27 09:06:19,116 INFO gateway.run: Received SIGTERM/SIGINT — initiating shutdown
2026-04-27 09:06:19,124 WARNING gateway.run: Shutdown diagnostic — other hermes processes running:
root 1 0.0 0.0 2564 1280 ? Ss 08:04 0:00 /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh gateway run
hermes 29 0.0 0.0 4320 3520 pts/0 Ss+ 08:04 0:00 bash
hermes 324 0.0 0.0 6792 3680 ? R 09:06 0:00 ps aux
2026-04-27 09:06:19,125 INFO gateway.run: Stopping gateway...
2026-04-27 09:06:19,128 INFO gateway.platforms.feishu: [Feishu] Disconnected
2026-04-27 09:06:19,128 INFO gateway.run: ✓ feishu disconnected
2026-04-27 09:06:19,130 INFO gateway.run: Gateway stopped
2026-04-27 09:06:19,130 INFO gateway.run: Cron ticker stopped
2026-04-27 09:06:19,131 INFO gateway.run: Exiting with code 1 (signal-initiated shutdown without restart request) so systemd Restart=on-failure can revive the gateway.
2026-04-27 09:06:21,479 INFO hermes_cli.plugins: Plugin 'openai' registered image_gen provider: openai
2026-04-27 09:06:21,479 INFO hermes_cli.plugins: Plugin 'openai-codex' registered image_gen provider: openai-codex
2026-04-27 09:06:21,540 INFO hermes_cli.plugins: Plugin 'xai' registered image_gen provider: xai
2026-04-27 09:06:21,602 INFO hermes_cli.plugins: Plugin discovery complete: 5 found, 4 enabled
2026-04-27 09:06:22,356 INFO gateway.run: Starting Hermes Gateway...
2026-04-27 09:06:22,356 INFO gateway.run: Session storage: /opt/data/sessions
Operating System
Ubuntu 24.04.1 LTS WSL
Python Version
3.13.5
Hermes Version
Hermes Agent v0.11.0 (2026.4.23), clone from main branch
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
The normal Docker startup path appears to be correct: the entrypoint starts as root, fixes ownership, and then runs the gateway as the hermes user.
The issue occurs when users enter the running container as root and manually launch hermes. This bypasses the entrypoint, so the CLI and any agent/tool commands run as root.
In hermes_cli/main.py, the CLI startup path loads environment/config state and initializes logging early. It also reads config.yaml for settings such as security.redact_secrets, but there is no guard that prevents root execution inside Docker or switches the process to the hermes user before agent/tool commands run.
The screenshot shows the CLI running as root while HERMES_UID=10000 and HERMES_GID=10000 are present. This suggests that these variables are not enforced in the manual CLI path; they only apply when the Docker entrypoint runs.
Therefore, files created or rewritten under $HERMES_HOME by the root-launched CLI can become root-owned. config.yaml is one concrete example, but logs, cache files, state databases, or other tool-modified files may be affected as well.
Proposed Fix (optional)
Add a Docker-root execution guard in hermes_cli/main.py.
When Hermes CLI starts inside a Docker container as root, it should detect this before running any agent/tool command. If detected, Hermes should either:
- drop privileges to the
hermes user, or
- refuse to continue and instruct the user to run Hermes as the
hermes user.
This would prevent root-launched CLI sessions from creating root-owned files under $HERMES_HOME, such as config.yaml, logs, cache files, or state databases.
Are you willing to submit a PR for this?
Bug Description
In Docker deployments, Hermes normally starts correctly: the entrypoint runs as root, fixes ownership, and then drops privileges to the
hermesuser before runninghermes gateway run.The problem occurs when users enter the running container as root via
docker execand manually runhermes. This bypasses the entrypoint privilege-dropping logic, so the CLI runs as root and may create or modify$HERMES_HOME/config.yamland other state files as root-owned.For example, the following screenshot shows Hermes running as
rooteven thoughHERMES_UIDandHERMES_GIDare set to10000:This demonstrates that:
rootHERMES_UID,HERMES_GID) are present but not enforcedLater, when the gateway runs normally as the
hermesuser, it can no longer access those files, causing permission errors.The issue is not that the official gateway startup fails to drop privileges, but that interactive root execution is not guarded against and can silently poison the file ownership under
$HERMES_HOME.This may be related to previous permission-related reports such as #15865, where the reported symptom was:
PR #16096 fixed one specific entrypoint-side issue by moving the
config.yamlchown/chmod handling into the root section before thegosuprivilege drop, and by guarding the chown failure path.However, the case described here is a different execution path: the user manually enters the running container as root and launches
hermes, bypassing the entrypoint entirely. In that path, the fix from #16096 may repairconfig.yamlin some cases after gateway restart, but other files touched by the root-launched CLI or agent tools may still become root-owned.This is also related in spirit to #4426 / #7357, where subprocess/tool environment isolation was improved by injecting a persistent HOME under
$HERMES_HOME. That fix helps subprocesses write tool configuration into the persistent volume, but it does not prevent the main Hermes CLI process itself from being launched as root inside the container.Therefore, the remaining issue is broader than
config.yaml: any file under$HERMES_HOMEcreated or modified by a root-launched Hermes CLI session may later become inaccessible to the normal gateway process running as thehermesuser.In later testing, restarting the gateway appeared to repair the ownership issue for
config.yamlin some cases. However, permission errors may still occur for other files under$HERMES_HOMEthat were previously created or modified by the root-launched CLI or agent tools. For example, files related to history, memory, cache, logs, or state may also become root-owned and later fail to be read by the normal gateway process running as thehermesuser.Therefore,
config.yamlis only one concrete example of the issue. The broader problem is that any file touched by a root-launched Hermes CLI session can become inaccessible to the normal non-root runtime.Steps to Reproduce
Build and start Hermes in a Docker dev environment:
docker compose -p hermes-agent-dev up -d --build
Enter the running container as root:
docker exec -it hermes-agent-dev bash
Launch the Hermes from this root shell:
hermes setup
Configure Hermes in the root-launched setup flow and trigger the config backup/update path.
Verify file ownership:
ls -l config.yaml
The file is now owned by
root.Launch hermes:
hermes
Observe permission-related errors when the gateway runs as the
hermesuser.chown: changing ownership of '/opt/data/config.yaml': Operation not permitted
Expected Behavior
When Hermes CLI is launched inside a Docker container, it should not execute any agent/tool command as
root.If the CLI is started from a root shell inside the container, Hermes should either:
hermesuser before running any agent/tool command, orhermesuser.After restarting the gateway, it should continue to run normally as the
hermesuser without failing due to root-owned files under$HERMES_HOME.Actual Behavior
After restarting the gateway, the service may fail with file permission / ownership errors because files under
$HERMES_HOMEhave become root-owned.config.yamlis one concrete example, but the same issue can also affect logs, cache files, state databases, or any other files created/modified by the root-launched CLI or agent tools.Example error:
Operating System
Ubuntu 24.04.1 LTS WSL
Python Version
3.13.5
Hermes Version
Hermes Agent v0.11.0 (2026.4.23), clone from main branch
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
The normal Docker startup path appears to be correct: the entrypoint starts as root, fixes ownership, and then runs the gateway as the
hermesuser.The issue occurs when users enter the running container as root and manually launch
hermes. This bypasses the entrypoint, so the CLI and any agent/tool commands run as root.In
hermes_cli/main.py, the CLI startup path loads environment/config state and initializes logging early. It also readsconfig.yamlfor settings such assecurity.redact_secrets, but there is no guard that prevents root execution inside Docker or switches the process to thehermesuser before agent/tool commands run.The screenshot shows the CLI running as
rootwhileHERMES_UID=10000andHERMES_GID=10000are present. This suggests that these variables are not enforced in the manual CLI path; they only apply when the Docker entrypoint runs.Therefore, files created or rewritten under
$HERMES_HOMEby the root-launched CLI can become root-owned.config.yamlis one concrete example, but logs, cache files, state databases, or other tool-modified files may be affected as well.Proposed Fix (optional)
Add a Docker-root execution guard in
hermes_cli/main.py.When Hermes CLI starts inside a Docker container as
root, it should detect this before running any agent/tool command. If detected, Hermes should either:hermesuser, orhermesuser.This would prevent root-launched CLI sessions from creating root-owned files under
$HERMES_HOME, such asconfig.yaml, logs, cache files, or state databases.Are you willing to submit a PR for this?