Hermes Agent 的 Docker 容器不停重启:修复指南
诊断 Hermes Agent 的 Docker 容器为什么一直重启:OOM、.env 出错、卷权限、端口冲突、ARM 架构不匹配。

你的 Hermes Agent 容器起来了,几秒后就挂,然后 Docker 又把它拉起来。docker ps 显示一行 Restarting (137) 3 seconds ago,机器人在 Telegram 上一直不回复,hermes logs 里同一段启动横幅一遍又一遍地刷。这个循环几乎总是五个非常具体问题中的一个,而 Docker 打印的退出码就直接告诉你是哪一个。这篇文章按“能修好最多容器”的顺序把它们逐一讲清楚。
如果你还没跑过这个容器,请先看如何用 Docker 运行 Hermes Agent。本文假设镜像能正常拉下来、compose 文件已就位,只是在进程一起来的那一刻就出问题。
第 1 步 - 动手改配置之前,先读真正的退出码
Docker 把上一次运行的退出码保存在容器状态里。直接读出来,不要靠日志猜:
docker inspect --format='{{.State.ExitCode}} OOM={{.State.OOMKilled}} err={{.State.Error}}' hermes-agent
这一行就同时告诉你三件事:退出码、内核 OOM killer 是否杀了这个进程,以及 Docker 附加在这次运行上的守护进程级错误。退出码能把排查范围大幅收窄:
- 137 - 进程被送了 SIGKILL。几乎总是容器内存限制或宿主机内存不足触发的 OOM kill,偶尔也可能是
docker stop撑过了 10 秒的宽限期。 - 139 - 段错误。在 Hermes Agent 上出现这个通常是镜像架构和宿主机不匹配(把 amd64 镜像跑在 ARM VPS 上,反过来也一样)。
- 125 / 126 / 127 - Docker 自己压根没能把容器跑起来。125 表示 daemon 拒绝了这次运行(选项不对、镜像缺失);126 表示 entrypoint 存在但没有可执行权限;127 表示 entrypoint 路径不对,或者需要的 shell 缺失。
- 1 或 2 - Hermes Agent 进程起来了,跑到自己的校验环节,然后带着应用级错误退出。执行
docker logs hermes-agent --tail 100,找第一条不是启动横幅的日志。
只有当你知道自己属于哪种情况,改配置才有意义。盲目加内存或者重写 compose 通常只是把真正的原因盖住,一周之后同样的问题又会把容器打倒。

第 2 步 - 退出码 137 且 OOMKilled=true:1 GB VPS 的陷阱
这是 Hermes Agent 上到目前为止最常见的原因,也是便宜 VPS 跑 AI 智能体那篇专门提醒过的坑。智能体闲着时并不吃内存,但你一发一段长对话、一段语音消息或一次 MCP 工具调用,内存就会迅速拉起来。在没有 swap 的 1 GB VPS 上,内核 OOM killer 会挑最大的进程(网关)杀掉。Docker 的重启策略立刻再拉起一个新容器,它同样分配同样多的内存,然后被同样杀掉。这就是你看到的循环。
到内核日志里确认一下:
sudo dmesg -T | grep -i -E 'oom-kill|killed process' | tail -5
# 或者在使用 systemd 的宿主上:
sudo journalctl -k --since '30 minutes ago' | grep -i oom
你会看到一行标出容器主进程名(hermes 或 node)以及被 kill 那一刻的 RSS。两个修法,按推荐顺序:
- 给宿主机更多 RAM。 2 GB 以下时,只要对话变长或语音模式介入,Hermes Agent 就会一直撞这块天花板。单用户跑得舒服的现实底线是 2 GB 加上 swap,或者 4 GB 不带 swap。
- 给宿主机加 swap。 Linux VPS 上:
sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile,再在/etc/fstab里持久化。swap 比 RAM 慢,但它是“容器被杀”和“回复变慢”之间的差别。
如果你在 compose 里显式设了 mem_limit,也一并检查。低于 1 GB 的限制在大宿主机上也会重现同样的 OOM 现象。重启前把这个限制去掉,或者提到至少 1.5 GB。
第 3 步 - 退出码 1 或 2,日志里有配置错误
如果退出码是 1 或 2,说明 Hermes Agent 起得足够远,能跑到自身的校验环节,然后拒绝了这份配置。日志会告诉你是哪一条出问题。三种形态几乎覆盖全部:
.env缺失或格式不对。 没有有效的 provider 密钥,网关就起不来。在日志里找Provider key not set或Missing TELEGRAM_BOT_TOKEN。确认.env里的值周围没有多余的引号(OPENROUTER_API_KEY="sk-..."可以,OPENROUTER_API_KEY = "sk-..."带空格就不行),也没有 Windows 换行(file .env应该显示 ASCII text,而不是 CRLF)。- 数据目录不可写。 如果看到
EACCES: permission denied, open '/data/config.json',说明容器以非 root 用户运行,而宿主机上 bind mount 过来的目录属于别人。在宿主上:sudo chown -R 1000:1000 ~/.hermes/data。镜像里用的就是 UID 1000;不要为了绕开这个而以 root 身份跑容器。 - 端口已被占用。
bind: address already in use意味着宿主机上已经有别的进程占着 8642 端口。用sudo lsof -i :8642找到它并停掉,或者在 compose 里把 Hermes Agent 映射到别的宿主端口(ports: - "9642:8642")。
这几类问题都不会靠重启自愈。改好配置再 docker compose up -d。
第 4 步 - 退出码 139 或 “exec format error”:ARM 镜像陷阱
如果容器在几毫秒内以 139 退出,或者 Docker 记录了 exec /usr/bin/node: exec format error,那说明你拉下来的镜像和宿主 CPU 架构不匹配。典型场景是 Oracle Cloud Ampere、AWS Graviton 或者 Raspberry Pi,这些都是 ARM64。你如果只拉了为 linux/amd64 构建的镜像,内核就没法执行那个二进制,而 Docker 会一直重试。
对比一下宿主和镜像的架构:
uname -m # aarch64 = ARM64,x86_64 = amd64
docker inspect hermes-agent-image \
--format='{{.Architecture}}/{{.Os}}' # 应该和 uname -m 一致
对不上就在拉镜像时显式指定平台。官方的 Hermes Agent 镜像是多架构发布的,所以直接指定平台就够了:
docker pull --platform linux/arm64 hermes/agent:latest
如果你构建的是自定义镜像,用 docker buildx build --platform linux/arm64,linux/amd64 重新构建,并把两个标签都推上去。把 ARM 镜像跑在 amd64 宿主上是同一个 bug 的镜像版本,产生同样的 139。
第 5 步 - 重启策略把真正的错误盖住了
restart: always 对生产环境的智能体是正确的默认值,但排查问题时,它会把每一次启动失败变成一个填满日志的紧密循环,把最初、真正的错误盖掉。出问题时,换到一个把失败暴露出来的策略:
services:
hermes-agent:
image: hermes/agent:latest
restart: "on-failure:3"
on-failure:3 在非零退出时最多重启 3 次然后放弃。容器会停下来,故障可以在 docker ps -a 里看到,日志也不会被新的启动覆盖掉。等真正原因修好了,再切回 restart: always 或 unless-stopped。Hermes Agent 调试与可观测性指南覆盖了在生产环境保持日志可读的日志轮转配置。

什么时候修不值得再耗一个周末
上面每一种故障都能修,但每一种都要花掉一个下午读内核日志、改 compose 文件。如果你走到这一步,是因为机器人已经掉线三天,只想赶紧把智能体用起来,那么托管版就是为这种情况准备的。Hermify 在 Telegram 上替你运行 Hermes Agent,记忆卷、provider 密钥和重启策略都已经接好,容器挂掉不再是你要诊断的问题。从 Hermify 开始,大约一分钟内就能回到在线状态。
对更想继续自托管的读者,下一篇推荐读 Hermes Agent 记忆和技能 —— 这是让一个跑在 Docker 里的智能体看起来“坏了”的第二大常见原因。
Sources
- Docker Exit Code 137 (OOMKilled): Understanding and Resolving
- How to Diagnose and Fix Docker OOMKilled Errors
- Docker Container Keeps Restarting: Find the Real Reason in 5 Minutes
- How to Fix Docker 'Exec Format Error' on Multi-Platform Images
- Running Docker Containers as a Non-root User with a Custom UID / GID