ByteNoteByteNote

字节笔记本

2026年6月21日

hermes教程-Docker

API中转
¥120

Hermes Agent — Docker

Docker 与 Hermes Agent 的交互有两种不同的方式:

  1. 在 Docker 中运行 Hermes — 代理本身在容器内运行(本页主要关注点)
  2. Docker 作为终端后端 — 代理在宿主机上运行,但每条命令都在一个单一的、持久的 Docker 沙箱容器中执行,该容器在工具调用、/new 和子代理之间保持存活,直到 Hermes 进程结束(参见 配置 → Docker 后端

本页介绍选项 1。容器将所有用户数据(配置、API 密钥、会话、技能、记忆)存储在宿主机挂载到 /opt/data 的单个目录中。镜像本身是无状态的,可以通过拉取新版本进行升级,而不会丢失任何配置。

快速开始

如果这是您第一次运行 Hermes Agent,请在宿主机上创建一个数据目录,并以交互方式启动容器来运行设置向导:

注意 — 避免使用基于浏览器的 VPS 控制台执行安装命令

某些 VPS 提供商(Hetzner Cloud 等)提供基于浏览器的控制台来管理主机。这些控制台会错误地传输特殊字符 — : 可能变成 ;@ 可能被错误渲染,非英语键盘布局更糟 — 这会静默地破坏 docker run 参数,例如 -v ~/.hermes:/opt/data-e KEY=value 以及粘贴的 API 密钥/令牌。

请改用 SSH 连接ssh root@<host>)以确保复制粘贴命令的安全。如果必须使用浏览器控制台,请手动输入命令而不是粘贴,并在按 Enter 前仔细检查结果中的每个 :@=/

sh
mkdir -p ~/.hermes
docker run -it --rm \
  -v ~/.hermes:/opt/data \
  nousresearch/hermes-agent setup

这将进入设置向导,提示您输入 API 密钥并将其写入 ~/.hermes/.env。您只需执行一次。强烈建议此时为网关设置一个聊天系统。

提示

在容器内,运行 hermes setup --portal 一次 — 刷新令牌会持久保存在挂载的 ~/.hermes 卷中。参见 Nous Portal

以网关模式运行

配置完成后,以后台方式运行容器作为持久网关(Telegram、Discord、Slack、WhatsApp 等):

sh
docker run -d \
  --name hermes \
  --restart unless-stopped \
  -v ~/.hermes:/opt/data \
  -p 8642:8642 \
  nousresearch/hermes-agent gateway run

端口 8642 暴露了网关的 OpenAI 兼容 API 服务器 和健康检查端点。如果您只使用聊天平台(Telegram、Discord 等),这是可选的;但如果您希望仪表板或外部工具访问网关,则必须暴露。

提示 — 网关受监督运行

在官方 Docker 镜像中,gateway run 由 s6-overlay 自动监督:如果网关进程崩溃,它会在几秒钟内重新启动,而不会丢失容器;仪表板(当设置了 HERMES_DASHBOARD=1 时)也会被同时监督。gateway run CMD 进程本身是一个 sleep infinity 心跳,保持容器存活,而 s6 管理实际的网关进程 — 因此 docker stop 仍然可以干净地关闭所有内容,但 docker logs 显示的是被监督的网关的输出。

您会在 docker logs 中看到一行面包屑确认升级。要选择退出 — 并获得传统的“网关是容器的主进程,容器退出 = 网关退出”语义 — 请传递 --no-supervise 或设置 HERMES_GATEWAY_NO_SUPERVISE=1。退出选项对于希望容器以网关状态码退出的 CI 冒烟测试很有用;对于生产部署,默认的监督模式严格更好。

此行为仅适用于基于 s6 的镜像。较早的(基于 tini 的)镜像仍然将 gateway run 作为前台主进程运行。

注意 — 网关日志的去向

请参阅下面的 日志去向 部分,了解完整的路由图(按配置文件划分的网关、仪表板、启动协调器、容器范围的 docker logs)。

注意:API 服务器受 API_SERVER_ENABLED=true 控制。要将其暴露到容器内 127.0.0.1 之外,还需设置 API_SERVER_HOST=0.0.0.0API_SERVER_KEY(至少 8 个字符 — 使用 openssl rand -hex 32 生成)。示例:

sh
docker run -d \
  --name hermes \
  --restart unless-stopped \
  -v ~/.hermes:/opt/data \
  -p 8642:8642 \
  -e API_SERVER_ENABLED=true \
  -e API_SERVER_HOST=0.0.0.0 \
  -e API_SERVER_KEY="$(openssl rand -hex 32)" \
  -e API_SERVER_CORS_ORIGINS='*' \
  nousresearch/hermes-agent gateway run

在面向互联网的机器上打开任何端口都存在安全风险。除非您了解风险,否则不应这样做。

运行仪表板

内置的 Web 仪表板作为受监督的 s6-rc 服务与网关在同一容器中运行。设置 HERMES_DASHBOARD=1 以启动它:

sh
docker run -d \
  --name hermes \
  --restart unless-stopped \
  -v ~/.hermes:/opt/data \
  -p 8642:8642 \
  -p 9119:9119 \
  -e HERMES_DASHBOARD=1 \
  nousresearch/hermes-agent gateway run

仪表板由 s6 监督 — 如果崩溃,s6-supervise 会在短暂退避后自动重启。仪表板的 stdout/stderr 会转发到 docker logs <container>(无前缀;网关自己的输出现在位于按配置文件划分的 s6-log 文件中 — 请参阅下面的 日志去向 — 因此两个流不会冲突)。

环境变量描述默认值
HERMES_DASHBOARD设置为 1(或 true / yes)以启用受监督的仪表板服务(未设置 — 服务已注册但保持关闭)
HERMES_DASHBOARD_HOST仪表板 HTTP 服务器的绑定地址0.0.0.0
HERMES_DASHBOARD_PORT仪表板 HTTP 服务器的端口9119
HERMES_DASHBOARD_INSECURE设置为 1(或 true / yes)以在没有 OAuth 认证门的情况下绑定。仅在没有 OAuth 契约的反向代理后面的受信任网络上使用 — 仪表板会暴露 API 密钥和会话数据(未设置 — 当注册了 DashboardAuthProvider 时强制执行门)

容器内的仪表板默认绑定 0.0.0.0 — 否则,发布的 -p 9119:9119 端口将无法从宿主机访问。要将绑定限制为容器回环(用于 sidecar / 反向代理设置),请设置 HERMES_DASHBOARD_HOST=127.0.0.1

当以下两个条件都满足时,仪表板的认证门会自动启用:

  1. 绑定主机是非回环的(例如容器内的默认 0.0.0.0),并且
  2. 注册了 DashboardAuthProvider 插件。

有三种内置方式可以满足第二个条件:

  • 用户名/密码 — 对于在受信任网络或 VPN 后面的自托管/本地/家庭实验室容器来说最简单:设置 HERMES_DASHBOARD_BASIC_AUTH_USERNAME + HERMES_DASHBOARD_BASIC_AUTH_PASSWORD(以及用于重启稳定会话的 HERMES_DASHBOARD_BASIC_AUTH_SECRET)。不适合直接暴露在公共互联网上。
  • OAuth(Nous Portal) — 用于托管/公共部署:当设置了 HERMES_DASHBOARD_OAUTH_CLIENT_ID 时,dashboard_auth/nous 提供程序会激活。
  • 自托管 OIDC — 通过标准 OpenID Connect 针对您自己的身份提供程序进行身份验证:当设置了 HERMES_DASHBOARD_OIDC_ISSUER + HERMES_DASHBOARD_OIDC_CLIENT_ID 时,dashboard_auth/self_hosted 提供程序会激活。

无论您选择哪种,门都会在调用者到达任何受保护的路由之前将其重定向到登录页面。有关所有三个提供程序,请参阅 Web 仪表板 → 身份验证

如果没有注册提供程序且绑定是非回环的,仪表板会在启动时失败关闭,并显示指向缺失环境变量的特定错误。HERMES_DASHBOARD_INSECURE=1 逃生口会完全禁用门(绑定主机本身从不暗示 --insecure),但它会提供一个未经身份验证的仪表板 — 除非您有自己的认证层在前面,否则请配置一个提供程序。

警告 — HERMES_DASHBOARD_INSECURE=1 会暴露 API 密钥

退出 OAuth 门会将仪表板的 API 表面(包括模型密钥和会话数据)提供给任何能够访问已发布端口的人。仅当您有自己的认证层在前面,或者您完全控制的受信任 LAN 上时才启用它。

将仪表板作为单独的容器运行是支持的,前提是该容器共享宿主机的 PID 和网络命名空间(例如 network_mode: host,就像仓库自己的 docker-compose.yml 所做的那样 — 请参阅其 dashboard 服务)。其网关存活检测需要与网关进程共享 PID 命名空间,因此该限制仅适用于在没有共享 PID 命名空间的隔离桥接网络容器中运行的仪表板。

以交互方式运行(CLI 聊天)

要针对正在运行的数据目录打开交互式聊天会话:

sh
docker run -it --rm \
  -v ~/.hermes:/opt/data \
  nousresearch/hermes-agent

或者,如果您已经在正在运行的容器中打开了终端(例如通过 Docker Desktop),只需运行:

sh
/opt/hermes/.venv/bin/hermes

持久卷

/opt/data 卷是所有 Hermes 状态的唯一真实来源。它映射到宿主机的 ~/.hermes/ 目录,包含:

路径内容
.envAPI 密钥和秘密
config.yaml所有 Hermes 配置
SOUL.md代理个性/身份
sessions/对话历史
memories/持久记忆存储
skills/已安装的技能
home/每个配置文件的 HOME,用于 Hermes 工具子进程(gitsshghnpm 和技能 CLI)
cron/计划任务定义
hooks/事件钩子
logs/运行时日志
skins/自定义 CLI 皮肤

~ 下存储凭据的技能 CLI 必须针对子进程 HOME 进行初始化,而不仅仅是数据卷根目录。例如,xurl 技能 将 OAuth 状态存储在 ~/.xurl 中;在官方 Docker 布局中,Hermes 工具调用将其读取为 /opt/data/home/.xurl,因此请使用 HOME=/opt/data/home 运行手动 xurl 认证,并使用 HOME=/opt/data/home xurl auth status 进行验证。

警告

切勿同时针对同一数据目录运行两个 Hermes 网关容器 — 会话文件和记忆存储并非为并发写入访问而设计。

多配置文件支持

Hermes 支持多个配置文件 — 独立的 ~/.hermes/ 子目录,让您可以从单个安装运行独立的代理(不同的 SOUL、技能、记忆、会话、凭据)。在官方 Docker 镜像中,s6 监督树将每个配置文件视为一等受监督服务,因此推荐的部署方式是一个容器托管所有配置文件

使用 hermes profile create <name> 创建的每个配置文件都会获得:

  • /run/service/gateway-<name>/ 处的一个专用 s6 服务槽,由运行时动态注册 — 无需重建容器。
  • 崩溃时自动重启,由 s6-supervise 进行退避管理。
  • 按配置文件轮转的日志,位于 ${HERMES_HOME}/logs/gateways/<name>/current(10 个归档 × 每个 1 MB)。
  • 跨容器重启的状态持久性:启动时协调器读取每个配置文件目录中的 gateway_state.json,并且仅将最后记录状态为 running 的配置文件的槽重新启动。只有您明确停止的网关(hermes gateway stop)会在重启后保持关闭 — 容器重启、镜像升级或意外退出会将记录状态保留为 running,因此网关会在下次启动时自动启动。

您在宿主机上运行的生命周期命令在容器内同样有效:

sh
## 创建配置文件 — 注册 gateway-<name> s6 槽。
docker exec hermes hermes profile create coder
## 启动 / 停止 / 重启 — 调度 s6-svc;网关生命周期在 docker restart 后仍然存在。
docker exec hermes hermes -p coder gateway start
docker exec hermes hermes -p coder gateway stop
docker exec hermes hermes -p coder gateway restart
## 状态 — 在容器内报告 `Manager: s6 (container supervisor)`。
docker exec hermes hermes -p coder gateway status
## 删除配置文件 — 同时拆除 s6 槽。
docker exec hermes hermes profile delete coder

在底层,容器内的 hermes gateway start/stop/restart 会被拦截并路由到针对正确服务目录的 s6-svc;您无需直接学习 s6 命令。要查看原始监督器状态,请使用 /command/s6-svstat /run/service/gateway-<name>(注意 /command/ 仅对监督树生成的进程在 PATH 中 — 从 docker exec 调用时,请传递绝对路径)。

从容器外部访问多个配置文件

两个不同的表面可以从外部访问配置文件的网关,它们的行为不同 — 不要混淆:

Hermes Desktop(和 Web 仪表板)。 Desktop 应用的远程网关连接与 hermes dashboard 后端(默认端口 9119,由 HERMES_DASHBOARD=1 启用)通信 — 不是 OpenAI API 服务器。一个仪表板后端服务于每个共置的配置文件:应用的配置文件切换器会随每个请求发送目标配置文件,后端会在磁盘上打开该配置文件的 HERMES_HOME。因此,您不需要为每个配置文件设置第二个端口或第二个连接;一个 :9119 连接通过切换器覆盖所有配置文件。

OpenAI 兼容 API 客户端(Open WebUI、LobeChat、/v1/...)。 这些客户端与每个配置文件的 API 服务器通信,该服务器为每个配置文件绑定端口 8642(从 API_SERVER_PORT / platforms.api_server.extra.port 解析 — 没有自动分配,也没有 config.yaml/gateway.port 键)。如果您希望客户端访问特定的第二个配置文件,请在该配置文件的自己的 .env 中为其分配一个不同的 API_SERVER_PORT,否则其网关也会尝试绑定 8642 并与默认配置文件冲突:

sh
## 创建配置文件(注册其 gateway-<name> s6 槽)
docker exec hermes hermes profile create work
## 将其 API 服务器指向一个空闲端口(写入配置文件自己的 .env)
cat >> /opt/data/profiles/work/.env <<'EOF'
API_SERVER_ENABLED=true
API_SERVER_PORT=8643
EOF

docker exec hermes hermes -p work gateway restart

API_SERVER_PORT 放在每个配置文件的自己的 .env 中,永远不要放在容器范围的 environment: 块中 — 全局值会强制每个配置文件使用相同的端口,导致冲突。使用桥接网络时,在 docker-compose.yml 中发布额外的端口(- "8643:8643");使用 network_mode: host 时,它已经在宿主机上可访问。默认配置文件的 8642 连接不受影响。

为什么是一个容器多个配置文件,而不是多个容器

在 s6 迁移之前,“每个配置文件一个容器”是推荐的模式,因为容器内没有监督器来管理多个网关。有了 s6 作为 PID 1,这不再是必需的,并且单容器布局在几乎所有维度上都更简单:

一个容器,多个配置文件每个配置文件一个容器
磁盘开销一个镜像,一个捆绑的 venv,一个 Playwright 缓存N 个镜像 / N 个缓存
内存开销共享 Python 解释器缓存,共享 node_modules每个容器重复
配置文件创建docker exec ... hermes profile create <name>(秒级)新的 docker run 调用 + 端口分配 + 绑定挂载配置
每个配置文件的崩溃恢复s6-supervise 自动重启Docker 的 --restart unless-stopped(较慢,会杀死兄弟工作)
日志通过 s6-log 按配置文件轮转的文件,加上容器启动审计日志每个容器 docker logs <name> — 无内置轮转
备份一个 ~/.hermes 目录N 个需要协调的目录

默认配置文件(default)在首次启动时始终注册,因此新容器开箱即带有一个受监督的网关。其他配置文件是纯运行时添加的。

何时确实需要单独的容器

容器内配置文件是默认设置。仅在您有特定原因时才为每个配置文件运行单独的容器:

  • 每个工作负载的资源隔离 — 例如,配置文件 A 中失控的浏览器工具会话不应导致配置文件 B 内存不足。容器为您提供每个配置文件的 --memory / --cpus
  • 独立的镜像固定 — 每个工作负载使用不同的上游镜像标签。
  • 网络分段 — 每个配置文件使用不同的 Docker 网络(例如,一个面向客户,一个内部)。
  • 合规性 / 爆炸半径 — 不同的凭据永远不会共享操作系统级别的进程树。

在这些情况下,为每个配置文件声明一个服务,使用不同的 container_namevolumesports

yaml
services:
  hermes-work:
    image: nousresearch/hermes-agent:latest
    container_name: hermes-work
    restart: unless-stopped
    command: gateway run
    ports:
      - "8642:8642"
    volumes:
      - ~/.hermes-work:/opt/data

  hermes-personal:
    image: nousresearch/hermes-agent:latest
    container_name: hermes-personal
    restart: unless-stopped
    command: gateway run
    ports:
      - "8643:8642"
    volumes:
      - ~/.hermes-personal:/opt/data

持久卷 中的警告仍然适用:切勿同时将两个容器指向同一个 ~/.hermes 目录。每个容器内的 s6 监督器管理自己的配置文件集;跨容器共享数据卷会损坏会话文件和记忆存储。

日志去向

s6 容器有四个不同的日志表面,“为什么我的网关在 docker logs 中不显示任何内容”是一个常见的意外。速查表:

来源去向如何读取
按配置文件的网关hermes gateway run 和 s6 下的按配置文件网关)分叉到两个地方:docker logs <container>(实时,无额外前缀) ${HERMES_HOME}/logs/gateways/<profile>/current(轮转,ISO-8601 时间戳,10 个归档 × 每个 1 MB)docker logs -f hermes 或在宿主机上 tail -F ~/.hermes/logs/gateways/default/current
仪表板(当 HERMES_DASHBOARD=1 时)docker logs <container>(无前缀)docker logs -f hermes — 与网关行交错
启动协调器(记录每次容器启动时恢复了哪些配置文件网关)${HERMES_HOME}/logs/container-boot.log(仅追加审计日志)tail -F ~/.hermes/logs/container-boot.log
通用 Hermes 日志agent.logerrors.log${HERMES_HOME}/logs/(配置文件感知)docker exec hermes hermes logs --follow [--level WARNING] [--session <id>]

两个值得了解的实际后果:

  • logs/gateways/<profile>/current 处的文件副本是容器重启后仍然存在的内容。docker logs 仅保留当前容器生命周期内的输出(并在 docker rm 时清除);轮转的文件持久保存在绑定挂载的卷上。
  • 启动协调器的审计行格式为 <iso-timestamp> profile=<name> prior_state=<state> action=<registered|started>,因此快速执行 grep profile=coder ~/.hermes/logs/container-boot.log 可以揭示给定配置文件上次恢复的时间以及 s6 是否自动启动了它。

环境变量转发

API 密钥从容器内的 /opt/data/.env 读取。您也可以直接传递环境变量:

sh
docker run -it --rm \
  -v ~/.hermes:/opt/data \
  -e ANTHROPIC_API_KEY="sk-ant-..." \
  -e OPENAI_API_KEY="sk-..." \
  nousresearch/hermes-agent

直接的 -e 标志会覆盖 .env 中的值。这对于 CI/CD 或秘密管理器集成(您不希望密钥在磁盘上)很有用。

注意 — 正在寻找 Docker 作为 终端后端

本页介绍在 Docker 内部运行 Hermes 本身。如果您希望 Hermes 在 Docker 沙箱容器中执行代理的 terminal / execute_code 调用(一个跨 Hermes 进程共享的长期运行容器 — 请参阅 issue #20561),那是一个单独的配置块 — terminal.backend: docker 加上 terminal.docker_imageterminal.docker_volumesterminal.docker_forward_envterminal.docker_envterminal.docker_run_as_host_userterminal.docker_extra_argsterminal.docker_persist_across_processesterminal.docker_orphan_reaper。有关完整集合(包括容器生命周期规则),请参阅 配置 → Docker 后端

Docker Compose 示例

对于同时包含网关和仪表板的持久部署,docker-compose.yaml 很方便:

yaml
services:
  hermes:
    image: nousresearch/hermes-agent:latest
    container_name: hermes
    restart: unless-stopped
    command: gateway run
    ports:
      - "8642:8642"   # 网关 API
      - "9119:9119"   # 仪表板(仅在设置 HERMES_DASHBOARD=1 时访问)
    volumes:
      - ~/.hermes:/opt/data
    environment:
      - HERMES_DASHBOARD=1
## 取消注释以转发特定环境变量而不是使用 .env 文件:
## - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
## - OPENAI_API_KEY=${OPENAI_API_KEY}
## - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "2.0"

使用 docker compose up -d 启动,并使用 docker compose logs -f 查看日志。受监督的网关的 stdout 也会分叉到卷上的 ${HERMES_HOME}/logs/gateways/<profile>/current — 请参阅 日志去向 了解完整路由图。

可选:Linux 桌面音频桥

Docker 中的语音模式需要两个独立的东西才能工作:必须允许 Hermes 在容器内探测音频设备,并且容器必须能够访问您的宿主机音频服务器。以下设置涵盖了 Linux 桌面的宿主机音频管道,这些桌面暴露了 PulseAudio 兼容的套接字,包括许多 PipeWire 设置。

注意

这是一个 Linux 桌面变通方法,不是通用的 Docker Desktop 功能。当您已经拥有宿主机音频工作并且希望在 Hermes 容器内使用 CLI 语音模式时,它很有用。如果 Hermes 仍然报告 Running inside Docker container -- no audio devices,请使用包含 Docker 音频探测支持(用于 PULSE_SERVER / PIPEWIRE_REMOTE)的构建。

首先,在 Compose 文件旁边创建一个 ALSA 配置:

conf
pcm.!default {
    type pulse
    hint {
        show on
        description "Default ALSA Output (PulseAudio)"
    }
}

pcm.pulse {
    type pulse
}

ctl.!default {
    type pulse
}

然后构建一个安装了 ALSA PulseAudio 插件的小型派生镜像:

dockerfile
FROM nousresearch/hermes-agent:latest

USER root
RUN apt-get update \
    && apt-get install -y --no-install-recommends libasound2-plugins \
    && rm -rf /var/lib/apt/lists/*

在 Compose 中使用该镜像,并传递宿主机用户的 PulseAudio 套接字和 cookie:

yaml
services:
  hermes:
    build:
      context: .
      dockerfile: Dockerfile.audio
    image: hermes-agent-audio
    container_name: hermes
    restart: unless-stopped
    command: gateway run
    volumes:
      - ~/.hermes:/opt/data
      - /run/user/${HERMES_UID}/pulse:/run/user/${HERMES_UID}/pulse
      - ~/.config/pulse/cookie:/tmp/pulse-cookie:ro
      - ./asound.conf:/etc/asound.conf:ro
    environment:
      - HERMES_UID=${HERMES_UID}
      - HERMES_GID=${HERMES_GID}
      - XDG_RUNTIME_DIR=/run/user/${HERMES_UID}
      - PULSE_SERVER=unix:/run/user/${HERMES_UID}/pulse/native
      - PULSE_COOKIE=/tmp/pulse-cookie

使用您的宿主机 UID/GID 启动它,以便容器进程可以访问每个用户的音频套接字:

sh
export HERMES_UID="$(id -u)"
export HERMES_GID="$(id -g)"
docker compose up -d --build

要验证 PortAudio 在容器内看到的内容:

sh
docker exec hermes /opt/hermes/.venv/bin/python -c "import sounddevice as sd; print(sd.query_devices())"

资源限制

Hermes 容器需要适中的资源。推荐的最低配置:

资源最低推荐
内存1 GB2–4 GB
CPU1 核2 核
磁盘(数据卷)500 MB2+ GB(随会话/技能增长)

浏览器自动化(Playwright/Chromium)是最消耗内存的功能。如果您不需要浏览器工具,1 GB 就足够了。如果使用浏览器工具,请分配至少 2 GB。

在 Docker 中设置限制:

sh
docker run -d \
  --name hermes \
  --restart unless-stopped \
  --memory=4g --cpus=2 \
  -v ~/.hermes:/opt/data \
  nousresearch/hermes-agent gateway run

Dockerfile 做了什么

官方镜像基于 debian:13.4,包含:

  • Python 3 及所有 Hermes 依赖项(uv pip install -e ".[all]"
  • Node.js + npm(用于浏览器自动化和 WhatsApp 桥接)
  • 带有 Chromium 的 Playwright(npx playwright install --with-deps chromium --only-shell
  • ripgrep、ffmpeg、git 和 xz-utils 作为系统工具
  • docker-cli — 以便在容器内运行的代理可以驱动宿主机的 Docker 守护进程(绑定挂载 /var/run/docker.sock 以选择加入),用于 docker builddocker run、容器检查等。
  • openssh-client — 启用从容器内使用 SSH 终端后端。SSH 后端会调用系统 ssh 二进制文件;没有它,在容器化安装中会静默失败。
  • WhatsApp 桥接(scripts/whatsapp-bridge/
  • s6-overlay v3 作为 PID 1(替换了较旧的 tini)— 监督仪表板和按配置文件的网关,崩溃时自动重启,回收僵尸子进程,并转发信号。

容器的 ENTRYPOINT 是 s6-overlay 的 /init。启动时它会:

  1. 以 root 身份运行 /etc/cont-init.d/01-hermes-setup(= docker/stage2-hook.sh):可选的 UID/GID 重新映射,修复卷所有权,在首次启动时种子 .env / config.yaml / SOUL.md,除非设置了 HERMES_SKIP_CONFIG_MIGRATION=1,否则运行非交互式配置模式迁移,同步捆绑的技能。
  2. 运行 /etc/cont-init.d/02-reconcile-profiles(= hermes_cli.container_boot):遍历 $HERMES_HOME/profiles/<name>/,在 /run/service/gateway-<profile>/ 下重新创建按配置文件的网关 s6 服务槽,并仅自动启动那些最后记录状态为 running 的(请参阅 按配置文件的网关监督)。
  3. 启动静态的 main-hermesdashboard s6-rc 服务。
  4. 执行容器的 CMD 作为主程序(/opt/hermes/docker/main-wrapper.sh),该程序将用户传递给 docker run 的参数路由:
    • 无参数 → hermes(默认)
    • 第一个参数是 PATH 上的可执行文件(例如 sleepbash)→ 直接执行它
    • 其他任何内容 → hermes <args>(子命令传递) 当此主程序退出时,容器退出,并带有其退出代码。

警告 — 与 s6 之前的镜像相比的破坏性更改

容器的 ENTRYPOINT 现在是 /init(s6-overlay),而不是 /usr/bin/tini。所有五种记录的 docker run 调用模式(无参数、chat -q "…"sleep infinitybash--tui)的行为与基于 tini 的镜像相同。如果您有依赖 tini 特定信号行为或硬编码 /usr/bin/tini -- 调用的下游包装器,请固定到之前的镜像标签。

警告 — 权限模型

除非您在命令链中保留 /init(或等效的旧版 docker/entrypoint.sh 垫片,它转发到 stage2 钩子),否则不要覆盖镜像入口点。s6-overlay 的 /init 以 root 身份运行,以便在首次启动时 chown 卷,然后通过 s6-setuidgid 为每个受监督的服务以及主程序降级到 hermes 用户。在官方镜像内以 root 身份启动 hermes gateway run 默认会被拒绝,因为它可能会在 /opt/data 中留下 root 拥有的文件,并破坏后续的仪表板或网关启动。仅当您有意接受该风险时,才设置 HERMES_ALLOW_ROOT_GATEWAY=1

docker exec 自动降级到 hermes 用户

docker exec hermes <cmd> 默认以 root 身份在容器内运行,但镜像在 /opt/hermes/bin/hermes(PATH 上最早)处提供了一个薄垫片,该垫片检测 root 调用者并透明地通过 s6-setuidgid hermes 重新执行。因此,docker exec hermes logindocker exec hermes profile create …docker exec hermes setup 等都会写入 UID 10000 拥有的文件 — 即受监督的网关可读 — 无需额外的 --user 标志。非 root 调用者(受监督的进程本身、docker exec --user hermes、容器内的看板子代理)会命中一个短路,直接执行 venv 二进制文件,因此热路径上没有开销。

如果您特别需要保留 root 语义的 docker exec(诊断会话、检查仅 root 状态、root 恰好拥有的 /opt/data 之外的文件),请按调用退出:

sh
docker exec -e HERMES_DOCKER_EXEC_AS_ROOT=1 hermes <cmd>

垫片接受 1 / true / yes(不区分大小写)。其他任何内容 — 包括像 =0 这样的拼写错误 — 都会落入降级,因此不可能静默退出。如果 s6-setuidgid 不可用(去除了 s6-overlay 的自定义构建),垫片会拒绝以 root 身份运行并退出 126,而不是回归到历史性的脚枪,即 docker exec hermes login 会将 auth.json 写为 root:root 并破坏受监督网关在每个聊天平台消息上的认证。

按配置文件的网关监督

使用 hermes profile create <name> 创建的每个配置文件都会自动获得一个 s6 监督的网关服务,注册在 /run/service/gateway-<name>/,具有跨容器重启的状态持久自动重启。请参阅上面的 多配置文件支持 了解面向用户的工作流程和生命周期命令。

相对于 s6 之前镜像的监督优势:

  • 网关崩溃由 s6-supervise 在大约 1 秒退避后自动重启。
  • 仪表板(当使用 HERMES_DASHBOARD=1 启用时)在同一个监督树上受到监督,并获得相同的自动重启处理。
  • docker restart、镜像升级(docker compose up -d --force-recreate)和意外退出会保留正在运行的网关:cont-init 协调器读取 $HERMES_HOME/profiles/<name>/gateway_state.json,如果最后记录状态为 running,则重新启动槽。只有显式的 hermes gateway stop 会记录 stopped 并在重启后保持网关关闭;重启或升级时发送的容器/s6 SIGTERM 被视为“仍在运行”并自动启动。
  • 按配置文件的网关日志持久保存在 $HERMES_HOME/logs/gateways/<profile>/current(由 s6-log 轮转),协调器的操作每次启动都会追加到 $HERMES_HOME/logs/container-boot.log。请参阅 日志去向 了解完整路由图。

容器内的 hermes status 报告 Manager: s6 (container supervisor)。使用 /command/s6-svstat /run/service/gateway-<name> 查看原始监督器视图(注意 /command/ 仅对监督树进程在 PATH 中;从 docker exec 调用时传递绝对路径)。

升级

拉取最新镜像并重新创建容器。您的数据目录会被保留,容器在启动网关之前会针对挂载的 $HERMES_HOME/config.yaml 运行非交互式配置模式迁移。当需要迁移时,Hermes 会首先在 config.yaml.env 旁边写入时间戳备份。

sh
docker pull nousresearch/hermes-agent:latest
docker rm -f hermes
docker run -d \
  --name hermes \
  --restart unless-stopped \
  -v ~/.hermes:/opt/data \
  nousresearch/hermes-agent gateway run

或者使用 Docker Compose:

sh
docker compose pull
docker compose up -d

仅当您需要先手动检查或迁移持久配置,然后再让新镜像重写它时,才设置 HERMES_SKIP_CONFIG_MIGRATION=1

技能和凭据文件

当使用 Docker 作为执行环境时(不是上述方法,而是当代理在 Docker 沙箱内运行命令时 — 请参阅 配置 → Docker 后端),Hermes 会重用单个长期运行的容器进行所有工具调用,并自动将技能目录(~/.hermes/skills/)和技能声明的任何凭据文件作为只读卷绑定挂载到该容器中。技能脚本、模板和引用在沙箱内可用,无需手动配置,并且由于容器在 Hermes 进程的生命周期内持续存在,您安装的任何依赖项或写入的文件都会保留到下一次工具调用。

对于 SSH 和 Modal 后端也会发生相同的同步 — 技能和凭据文件会在每条命令之前通过 rsync 或 Modal 挂载 API 上传。

在容器中安装更多工具

官方镜像附带了一套精选的工具(请参阅 Dockerfile 做了什么),但并非代理可能需要的每个工具都已预装。有五种推荐的方法,按努力和持久性递增的顺序排列。

npm 或 Python 工具 — 使用 npxuvx

对于任何发布到 npm 或 PyPI 的工具,指示 Hermes 通过 npx(npm)或 uvx(Python)运行它,并在其持久记忆中记住该命令。如果工具需要配置文件或凭据,指示它将这些文件放在 /opt/data 下(例如 /opt/data/<tool>/config.yaml)。

依赖项按需获取,并在容器的生命周期内缓存。写入 /opt/data 下的配置在容器重启后仍然存在,因为它位于绑定挂载的宿主机目录上。包缓存本身在 docker rm 后会重建,但 npxuvx 会在下次运行工具时透明地重新获取。

其他工具(apt 包、二进制文件)— 安装并记住

对于 npm 或 PyPI 之外的任何内容 — apt 包、预构建的二进制文件、镜像中尚未包含的语言运行时 — 指示 Hermes 如何安装它(例如 apt-get update && apt-get install -y <package>),并告诉它记住安装命令。该工具在容器的剩余生命周期内持续存在,并且 Hermes 会在下次需要该工具时在容器重启后重新运行安装命令。

这适用于安装快速且偶尔使用的工具。对于经常使用的工具,请使用下一种方法。

持久安装 — 构建派生镜像

当工具必须在每次容器启动时立即可用,无需重新安装延迟时,构建一个继承自 nousresearch/hermes-agent 并在层中安装该工具的新镜像:

dockerfile
FROM nousresearch/hermes-agent:latest

USER root
RUN apt-get update \
    && apt-get install -y --no-install-recommends <your-package> \
    && rm -rf /var/lib/apt/lists/*
USER hermes

构建它并代替官方镜像使用:

sh
docker build -t my-hermes:latest .
docker run -d \
  --name hermes \
  --restart unless-stopped \
  -v ~/.hermes:/opt/data \
  -p 8642:8642 \
  my-hermes:latest gateway run

入口点脚本和 /opt/data 语义保持不变,因此本页的其余部分仍然适用。在拉取更新的上游 nousresearch/hermes-agent 时,记得重建镜像。

复杂工具或多服务堆栈 — 运行 sidecar 容器

对于自带服务(数据库、Web 服务器、队列、无头浏览器农场)或过于沉重无法放在 Hermes 容器内的工具,将它们作为单独的容器在共享 Docker 网络上运行。Hermes 通过容器名称访问 sidecar,就像访问本地推理服务器一样(请参阅 连接到本地推理服务器)。

yaml
services:
  hermes:
    image: nousresearch/hermes-agent:latest
    container_name: hermes
    restart: unless-stopped
    command: gateway run
    ports:
      - "8642:8642"
    volumes:
      - ~/.hermes:/opt/data
    networks:
      - hermes-net

  my-tool:
    image: example/my-tool:latest
    container_name: my-tool
    restart: unless-stopped
    networks:
      - hermes-net

networks:
  hermes-net:
    driver: bridge

从 Hermes 容器内部,sidecar 可通过 http://my-tool:<port>(或它提供的任何协议)访问。这种模式使每个服务的生命周期、资源限制和升级节奏保持独立,并避免用仅一个工具需要的依赖项膨胀 Hermes 镜像。

广泛有用的工具 — 提交 issue 或 pull request

如果某个工具可能对大多数 Hermes Agent 用户有用,请考虑将其贡献到上游,而不是在私有派生镜像中携带它。在 hermes-agent 仓库 上提交 issue 或 pull request,描述该工具及其用例。捆绑到官方镜像中的工具会使每个用户受益,并避免下游分支的维护开销。

连接到本地推理服务器(vLLM、Ollama 等)

当在 Docker 中运行 Hermes 并且您的推理服务器(vLLM、Ollama、text-generation-inference 等)也在宿主机或其他容器中运行时,网络需要额外注意。

Docker Compose(推荐)

将两个服务放在同一个 Docker 网络上。这是最可靠的方法:

yaml
services:
  vllm:
    image: vllm/vllm-openai:latest
    container_name: vllm
    command: >
      --model Qwen/Qwen2.5-7B-Instruct
      --served-model-name my-model
      --host 0.0.0.0
      --port 8000
    ports:
      - "8000:8000"
    networks:
      - hermes-net
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]

  hermes:
    image: nousresearch/hermes-agent:latest
    container_name: hermes
    restart: unless-stopped
    command: gateway run
    ports:
      - "8642:8642"
    volumes:
      - ~/.hermes:/opt/data
    networks:
      - hermes-net

networks:
  hermes-net:
    driver: bridge

然后在您的 ~/.hermes/config.yaml 中,使用容器名称作为主机名:

yaml
model:
  provider: custom
  model: my-model
  base_url: http://vllm:8000/v1
  api_key: "none"

提示 — 关键点

  • 使用容器名称vllm)作为主机名 — 而不是 localhost127.0.0.1,它们指向 Hermes 容器本身。
  • model 值必须与您传递给 vLLM 的 --served-model-name 匹配。
  • api_key 设置为任何非空字符串(vLLM 需要该标头,但默认情况下不验证它)。
  • 不要在 base_url 中包含尾部斜杠。

独立 Docker run(无 Compose)

如果您的推理服务器直接在宿主机上运行(不在 Docker 中),在 macOS/Windows 上使用 host.docker.internal,在 Linux 上使用 --network host

macOS / Windows:

sh
docker run -d \
  --name hermes \
  -v ~/.hermes:/opt/data \
  -p 8642:8642 \
  nousresearch/hermes-agent gateway run
yaml
## config.yaml
model:
  provider: custom
  model: my-model
  base_url: http://host.docker.internal:8000/v1
  api_key: "none"

Linux(主机网络):

sh
docker run -d \
  --name hermes \
  --network host \
  -v ~/.hermes:/opt/data \
  nousresearch/hermes-agent gateway run
yaml
## config.yaml
model:
  provider: custom
  model: my-model
  base_url: http://127.0.0.1:8000/v1
  api_key: "none"

警告 — 使用 --network host 时,-p 标志被忽略 — 所有容器端口都直接暴露在宿主机上。

验证连接性

从 Hermes 容器内部,确认推理服务器可访问:

sh
docker exec hermes curl -s http://vllm:8000/v1/models

您应该会看到列出您所服务模型的 JSON 响应。如果失败,请检查:

  1. 两个容器在同一个 Docker 网络上(docker network inspect hermes-net
  2. 推理服务器正在监听 0.0.0.0,而不是 127.0.0.1
  3. 端口号匹配

Ollama

Ollama 的工作方式相同。如果 Ollama 在宿主机上运行,请使用 host.docker.internal:11434(macOS/Windows)或 127.0.0.1:11434(Linux 使用 --network host)。如果 Ollama 在同一个 Docker 网络上的自己的容器中运行:

yaml
model:
  provider: custom
  model: llama3
  base_url: http://ollama:11434/v1
  api_key: "none"

故障排除

容器立即退出

检查日志:docker logs hermes。常见原因:

  • 缺少或无效的 .env 文件 — 首先以交互方式运行以完成设置
  • 如果使用暴露端口,端口冲突

“Permission denied” 错误

容器的 stage2 钩子通过每个受监督服务内的 s6-setuidgid 将权限降级到非 root 的 hermes 用户(UID 10000)。如果您的宿主机 ~/.hermes/ 由不同的 UID 拥有,请设置 HERMES_UID/HERMES_GID — 或它们的 PUID/PGID 别名,以与 LinuxServer.io 和 NAS 镜像保持一致 — 以匹配您的宿主机用户,或确保数据目录可写:

sh
chmod -R 755 ~/.hermes

在 NAS(UGOS、Synology、unRAID)上,数据目录通常是绑定挂载,由容器无法 chown 的宿主机 UID 拥有。将 PUID/PGID(或 HERMES_UID/HERMES_GID)设置为该宿主机用户,以便运行时以挂载的所有者身份运行,而不是 UID 10000:

sh
docker run -d \
  --name hermes \
  -e PUID=1000 -e PGID=10 \
  -v /volume1/docker/hermes:/opt/data \
  nousresearch/hermes-agent gateway run

docker exec hermes <cmd> 也会自动降级到 UID 10000 — 有关详细信息以及按调用退出的方法,请参阅 docker exec 自动降级到 hermes 用户

浏览器工具无法工作

Playwright 需要共享内存。在 Docker run 命令中添加 --shm-size=1g

sh
docker run -d \
  --name hermes \
  --shm-size=1g \
  -v ~/.hermes:/opt/data \
  nousresearch/hermes-agent gateway run

网络问题后网关无法重新连接

--restart unless-stopped 标志处理大多数瞬时故障。如果网关卡住,请重启容器:

sh
docker restart hermes

检查容器健康状态

sh
docker logs --tail 50 hermes          # 最近的日志
docker run -it --rm nousresearch/hermes-agent:latest version     # 验证版本
docker stats hermes                    # 资源使用情况

分享: