章節:使用 Hermes · URL:https://hermesbible.com/docs/user-guide/docker
Hermes Agent — Docker
Docker 與 Hermes Agent 有兩種不同的交集方式:
- 在 Docker 中運行 Hermes — Agent 本身在容器中運行(本頁的主要重點)
- Docker 作為終端後端 — Agent 在你的主機上運行,但在單一、持久化的 Docker 沙箱容器中執行所有指令,該容器在 Hermes 生命週期內跨越工具呼叫、
/new和子代理持久運行(參見 設定 → Docker 後端)
本頁涵蓋選項 1。容器將所有使用者資料(設定、API 金鑰、會話、技能、記憶)儲存在從主機掛載到 /opt/data 的單一目錄中。映像本身是無狀態的,可以透過拉取新版本來升級,而不會遺失任何設定。
快速開始
如果這是你第一次運行 Hermes Agent,請在主機上建立資料目錄並互動式啟動容器以執行設定精靈:
注意 — 避免使用基於瀏覽器的 VPS 主控台執行安裝指令
部分 VPS 供應商(Hetzner Cloud 及其他多家)提供基於瀏覽器的 主控台來管理主機。這些主控台會錯誤傳輸特殊字元 —
:可能變為;、@可能被誤譯,非英文鍵盤配置的情況更糟 — 這會靜默損壞-v ~/.hermes:/opt/data、-e KEY=value等docker run參數, 以及貼上的 API 金鑰 / 令牌。改用 SSH 連線(
ssh root@<host>)以確保複製貼上安全的指令輸入。 如果必須使用瀏覽器主控台,請手動輸入指令而非貼上,並在按下 Enter 前 仔細檢查結果中的每個:、@、=和/。
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 等):
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 runCMD 程序本身是一個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.0 和 API_SERVER_KEY(最少 8 個字元 — 使用 openssl rand -hex 32 產生一個)。範例:
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 來啟動它:
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。
儀表板的驗證閘門在以下兩項條件同時滿足時自動啟用:
- 綁定主機是非回環的(例如容器內預設的
0.0.0.0),且 - 已註冊
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 聊天)
要針對運行中的資料目錄開啟互動式聊天會話:
docker run -it --rm \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent
或者如果你已經在運行中的容器裡開啟了終端(例如透過 Docker Desktop),只需執行:
/opt/hermes/.venv/bin/hermes
持久化卷
/opt/data 卷是所有 Hermes 狀態的唯一真實來源。它映射到你的主機 ~/.hermes/ 目錄,包含:
| 路徑 | 內容 |
|---|---|
.env | API 金鑰和機密 |
config.yaml | 所有 Hermes 設定 |
SOUL.md | 代理人格/身分 |
sessions/ | 對話歷史 |
memories/ | 持久化記憶儲存 |
skills/ | 已安裝的技能 |
home/ | 每個設定檔的 HOME 目錄,用於 Hermes 工具子程序(git、ssh、gh、npm 和技能 CLI) |
cron/ | 排程工作定義 |
hooks/ | 事件鉤子 |
logs/ | 運行時日誌 |
skins/ | 自訂 CLI 外觀 |
在 ~ 下儲存憑證的技能 CLI 必須針對子程序 HOME 初始化,而不僅僅是資料卷根目錄。例如,xurl 技能 在 ~/.xurl 中儲存 OAuth 狀態;在官方 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,因此閘道器會在下次啟動時自動啟動。
你在主機上執行的生命週期指令在容器內以相同方式運作:
# 建立設定檔 — 註冊 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。因此你不需要為 Desktop 的每個設定檔準備第二個埠 — 或第二個連線 — 一個 :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 會與預設設定檔衝突:
# 建立設定檔(註冊其 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_name、volumes 和 ports:
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.log、errors.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 讀取。你也可以直接傳入環境變數:
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: docker加上terminal.docker_image、terminal.docker_volumes、terminal.docker_forward_env、terminal.docker_env、terminal.docker_run_as_host_user、terminal.docker_extra_args、terminal.docker_persist_across_processes和terminal.docker_orphan_reaper。參見 設定 → Docker 後端 以了解完整的設定集,包括容器生命週期規則。
Docker Compose 範例
對於閘道器和儀表板的持久化部署,docker-compose.yaml 很方便:
services:
hermes:
image: nousresearch/hermes-agent:latest
container_name: hermes
restart: unless-stopped
command: gateway run
ports:
- "8642:8642" # gateway API
- "9119:9119" # dashboard (只有當 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 也會被 tee 到卷上的 ${HERMES_HOME}/logs/gateways/<profile>/current — 參見日誌去向以了解完整的路由圖。
可選:Linux 桌面音訊橋接
Docker 中的語音模式需要兩件分開的事才能運作:Hermes 必須被允許在容器內探索音訊裝置,且容器必須能夠存取你的主機音訊伺服器。以下設定涵蓋了暴露 PulseAudio 相容插槽的 Linux 桌面的主機音訊管道,包括許多 PipeWire 設定。
注意
這是一個 Linux 桌面的解決方案,不是通用的 Docker Desktop 功能。當你的主機音訊已經運作且想在 Hermes 容器內使用 CLI 語音模式時很有用。如果 Hermes 仍然報告
Running inside Docker container -- no audio devices,請使用包含 Docker 音訊探索支援(用於PULSE_SERVER/PIPEWIRE_REMOTE)的版本。
首先,在你的 Compose 檔案旁建立一個 ALSA 設定:
pcm.!default {
type pulse
hint {
show on
description "Default ALSA Output (PulseAudio)"
}
}
pcm.pulse {
type pulse
}
ctl.!default {
type pulse
}
然後建立一個安裝了 ALSA PulseAudio 外掛的小型衍生映像:
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:
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 啟動它,以便容器程序可以存取使用者的音訊插槽:
export HERMES_UID="$(id -u)"
export HERMES_GID="$(id -g)"
docker compose up -d --build
要驗證 PortAudio 在容器內看到的內容:
docker exec hermes /opt/hermes/.venv/bin/python -c "import sounddevice as sd; print(sd.query_devices())"
資源限制
Hermes 容器需要適度的資源。建議最低配置:
| 資源 | 最低 | 建議 |
|---|---|---|
| 記憶體 | 1 GB | 2–4 GB |
| CPU | 1 核心 | 2 核心 |
| 磁碟(資料卷) | 500 MB | 2+ GB(隨會話/技能增長) |
瀏覽器自動化(Playwright/Chromium)是最耗記憶體的功能。如果你不需要瀏覽器工具,1 GB 就足夠了。使用瀏覽器工具時,至少分配 2 GB。
在 Docker 中設定限制:
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 橋接)
- Playwright 及 Chromium(
npx playwright install --with-deps chromium --only-shell) - ripgrep、ffmpeg、git 和
xz-utils作為系統工具 docker-cli— 以便在容器內運行的代理可以驅動主機的 Docker 守護行程(掛載/var/run/docker.sock以啟用),用於docker build、docker run、容器檢查等。openssh-client— 從容器內啟用 SSH 終端後端。SSH 後端呼叫系統的ssh二進位檔;沒有這個,它在容器化安裝中會靜默失敗。- WhatsApp 橋接(
scripts/whatsapp-bridge/) s6-overlayv3 作為 PID 1(取代舊版的tini)— 監督儀表板和每個設定檔的閘道器,崩潰時自動重啟,回收僵尸子程序,並轉送訊號。
容器的 ENTRYPOINT 是 s6-overlay 的 /init。啟動時它:
- 以 root 執行
/etc/cont-init.d/01-hermes-setup(=docker/stage2-hook.sh):可選的 UID/GID 重映射、修正卷所有權、在首次啟動時填充.env/config.yaml/SOUL.md、在未設定HERMES_SKIP_CONFIG_MIGRATION=1時執行非互動式 config-schema 遷移、同步捆綁的技能。 - 執行
/etc/cont-init.d/02-reconcile-profiles(=hermes_cli.container_boot):遍歷$HERMES_HOME/profiles/<name>/、在/run/service/gateway-<profile>/下重新建立每個設定檔的閘道器 s6 服務插槽,並僅自動啟動最後記錄狀態為running的設定檔(參見每個設定檔的閘道器監督)。 - 啟動靜態的
main-hermes和dashboards6-rc 服務。 - 將容器的 CMD 作為主要程式(
/opt/hermes/docker/main-wrapper.sh)執行,該程式路由使用者傳入docker run的參數:- 無參數 →
hermes(預設) - 第一個參數是 PATH 上的可執行檔(例如
sleep、bash)→ 直接執行它 - 其他任何情況 →
hermes <args>(子命令透傳) 當此主要程式退出時,容器以它的退出碼退出。
- 無參數 →
警告 — 與 pre-s6 映像的破壞性變更
容器的 ENTRYPOINT 現在是
/init(s6-overlay),不是/usr/bin/tini。文件記載的五種docker run呼叫模式(無參數、chat -q "…"、sleep infinity、bash、--tui)的行為與基於 tini 的映像完全相同。如果你有依賴 tini 特定訊號行為或硬編碼/usr/bin/tini --呼叫的下游包裝器,請釘選到先前的映像標籤。
警告 — 權限模型
除非你在指令鏈中保留
/init(或等效的、轉送到 stage2 hook 的舊版docker/entrypoint.sh墊片),否則不要覆蓋映像入口點。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 login、docker exec hermes profile create …、docker exec hermes setup 等都會寫入由 UID 10000 擁有的檔案 — 即受監督的閘道器可讀取的 — 不需要額外的 --user 標誌。非 root 呼叫者(受監督的程序本身、docker exec --user hermes、容器內的 kanban 子代理)會觸發一個短路,直接執行 venv 二進位檔,因此熱路徑上沒有額外開銷。
如果你特定需要保留 root 語意的 docker exec(診斷會話、檢查僅限 root 的狀態、root 恰好擁有的 /opt/data 以外的檔案),可在每次呼叫時退出:
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 會以 root:root 寫入 auth.json 並破壞受監督閘道器在每個聊天平台訊息上的驗證。
每個設定檔的閘道器監督
每個透過 hermes profile create <name> 建立的設定檔自動獲得一個在 /run/service/gateway-<name>/ 註冊的 s6 受監督閘道器服務,具有跨容器重啟的狀態持久化自動重啟。參見上方的多設定檔支援以了解面向使用者的工作流程和生命週期指令。
相較於 pre-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 執行非互動式 config-schema 遷移。當需要遷移時,Hermes 會先在 config.yaml 和 .env 旁寫入帶有時間戳的備份。
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:
docker compose pull
docker compose up -d
僅在你需要在新映像重寫之前手動檢查或遷移持久化的設定時才設定 HERMES_SKIP_CONFIG_MIGRATION=1。
技能和憑證檔案
當使用 Docker 作為執行環境(不是上述方法,而是代理在 Docker 沙箱內執行指令 — 參見 設定 → Docker 後端)時,Hermes 會為所有工具呼叫重用一個長壽容器,並自動將技能目錄(~/.hermes/skills/)和技能宣告的任何憑證檔案作為唯讀卷掛載到該容器中。技能腳本、範本和參考資料在沙箱內可用,無需手動配置,並且由於容器在 Hermes 程序的生命週期內持久化,你安裝的任何依賴或寫入的檔案會在下一次工具呼叫時保留。
相同的同步發生在 SSH 和 Modal 後端 — 技能和憑證檔案在每次指令之前透過 rsync 或 Modal mount API 上傳。
在容器中安裝更多工具
官方映像附帶一組精選的工具(參見 Dockerfile 的作用),但不是代理可能想要的每個工具都已預安裝。有五種建議的方法,按努力程度和持久性遞增排序。
npm 或 Python 工具 — 使用 npx 或 uvx
對於發佈到 npm 或 PyPI 的任何工具,指示 Hermes 透過 npx(npm)或 uvx(Python)運行它,並將該指令記住在其持久化記憶中。如果工具需要設定檔案或憑證,指示它將這些放在 /opt/data 下(例如 /opt/data/<tool>/config.yaml)。
依賴按需獲取並在容器的生命週期內快取。寫入 /opt/data 下的設定在容器重啟後持久化,因為它位於掛載的主機目錄上。套件快取本身在 docker rm 後會重建,但 npx 和 uvx 會在工具下次運行時透明地重新獲取。
其他工具(apt 套件、二進位檔)— 安裝並記住
對於 npm 或 PyPI 以外的任何內容 — apt 套件、預編譯的二進位檔、映像中尚未包含的語言運行時 — 指示 Hermes 如何安裝它(例如 apt-get update && apt-get install -y <package>),並告訴它記住安裝指令。工具在容器剩餘的生命週期內持久化,Hermes 會在容器重啟後下次需要該工具時重新執行安裝指令。
這適用於安裝快速且偶爾使用的工具。對於經常使用的工具,建議使用下一種方法。
持久化安裝 — 建立衍生映像
當工具必須在每次容器啟動時立即可用且沒有重新安裝延遲時,建立一個從 nousresearch/hermes-agent 繼承的新映像,在一個層中安裝工具:
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
建置它並使用它替代官方映像:
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,就像它存取本地推論伺服器一樣(參見連接本地推論伺服器)。
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 網路上。這是最可靠的方法:
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 中,使用容器名稱作為主機名:
model:
provider: custom
model: my-model
base_url: http://vllm:8000/v1
api_key: "none"
提示 — 重點
- 使用容器名稱(
vllm)作為主機名 — 不是localhost或127.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:
docker run -d \
--name hermes \
-v ~/.hermes:/opt/data \
-p 8642:8642 \
nousresearch/hermes-agent gateway run
# config.yaml
model:
provider: custom
model: my-model
base_url: http://host.docker.internal:8000/v1
api_key: "none"
Linux(主機網路):
docker run -d \
--name hermes \
--network host \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run
# config.yaml
model:
provider: custom
model: my-model
base_url: http://127.0.0.1:8000/v1
api_key: "none"
警告 — 使用
--network host時,-p標誌會被忽略 — 所有容器埠都直接暴露在主機上。
驗證連線
從 Hermes 容器內部,確認推論伺服器可達:
docker exec hermes curl -s http://vllm:8000/v1/models
你應該看到列出你提供的模型的 JSON 回應。如果失敗,請檢查:
- 兩個容器在相同的 Docker 網路上(
docker network inspect hermes-net) - 推論伺服器正在監聽
0.0.0.0,而不是127.0.0.1 - 埠號匹配
Ollama
Ollama 的工作方式相同。如果 Ollama 在主機上運行,使用 host.docker.internal:11434(macOS/Windows)或 127.0.0.1:11434(Linux 加上 --network host)。如果 Ollama 在同一 Docker 網路上的自己的容器中運行:
model:
provider: custom
model: llama3
base_url: http://ollama:11434/v1
api_key: "none"
疑難排解
容器立即退出
檢查日誌:docker logs hermes。常見原因:
- 缺少或無效的
.env檔案 — 先互動式運行以完成設定 - 使用暴露埠運行時的埠衝突
「Permission denied」錯誤
容器的 stage2 hook 透過 s6-setuidgid 在每個受監督的服務內將權限降到非 root 的 hermes 使用者(UID 10000)。如果你的主機 ~/.hermes/ 由不同的 UID 擁有,設定 HERMES_UID/HERMES_GID — 或它們的 PUID/PGID 別名,以與 LinuxServer.io 和 NAS 映像保持一致 — 以匹配你的主機使用者,或確保資料目錄可寫入:
chmod -R 755 ~/.hermes
在 NAS(UGOS、Synology、unRAID)上,資料目錄通常是容器無法 chown 的主機 UID 擁有的掛載目錄。設定 PUID/PGID(或 HERMES_UID/HERMES_GID)為該主機使用者,以便運行時以掛載的所有者而非 UID 10000 運行:
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:
docker run -d \
--name hermes \
--shm-size=1g \
-v ~/.hermes:/opt/data \
nousresearch/hermes-agent gateway run
閘道器在網路問題後未重新連接
--restart unless-stopped 標誌處理大多數暫時性故障。如果閘道器卡住,重啟容器:
docker restart hermes
檢查容器健康狀態
docker logs --tail 50 hermes # 最近的日誌
docker run -it --rm nousresearch/hermes-agent:latest version # 驗證版本
docker stats hermes # 資源使用情況