1. 为什么我建议你用 Docker 跑 OpenHands
OpenHands 是一个开源的 AI 编码助手,前身叫 OpenDevin,核心卖点不是陪你聊天,而是让模型真的去改代码、跑命令、读文件、调接口。你可以把它理解成一个“能动手的编程搭子”:你说需求,它在沙箱里执行,把过程摊开给你看。适合谁?想自建 AI 编码助手、又不想被某个闭源 IDE 绑死的开发者;想研究软件工程 Agent 执行链的工程师;以及想给团队内网搭一套可控编码助手的同学。
但这类项目有个通病:第一眼像神器,第二眼就掉进环境、权限、端口和 API Key 的坑里。我试过用 uv 直接起,也试过 Docker,最后发现对大多数 CSDN 读者来说,Docker 单容器是最稳的复现路线——隔离干净、卸载方便、出错好回滚。这篇就按“能照着敲完”的标准来:先起容器,再打开 Web 界面,然后把模型通道接到 TaoToken 的统一 Key/API 通道上,最后用一个最小代码任务验证整条链路真的通了。
需要提前说清楚一件事:OpenHands 官方明确提醒,它默认面向单用户本地工作站,不带完整认证、隔离和扩展能力,别裸奔到公网。本地体验没问题,公网部署必须自己加反向代理和认证。这个边界先记住,后面排错会省很多事。
2. 前置准备:Docker 环境与 TaoToken 通道
在拉镜像之前,先把地基打平。这一节不涉及 OpenHands 本身,但跳过它,后面 80% 的报错都会找上门。
先确认 Docker 装好了:
docker --version docker ps如果docker ps报permission denied while trying to connect to the Docker daemon socket,别急着重装,大概率只是当前用户不在 docker 组:
sudo usermod -aG docker $USER newgrp docker docker ps接着建持久化目录,OpenHands 会把配置和会话状态写进~/.openhands:
mkdir -p ~/.openhands再确认 3000 端口没被占:
ss -lntp | grep 3000有输出就说明被占了,要么停掉旧服务,要么后面把映射改成 3001。
然后是模型通道。OpenHands 启动后要在页面里选 Provider、填 API Key,如果你用 OpenAI 兼容接口,还得填 Base URL 和 Model ID。这里我建议直接走 TaoToken 的统一通道,一个 Key 覆盖多家模型,省得在页面里来回切 Provider。你需要准备三样东西:
- Base URL:
https://taotoken.net/api - API Key:在 TaoToken 控制台的 API Keys 页面生成
- Model ID:按你实际要用的模型填,比如
claude-sonnet-4-5这类
控制台入口在这里:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
生成 Key 的页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
如果你还没决定用哪个模型,可以先在模型对话页面试一下连通性,确认 Key 有效再往下走:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
注意:Base URL 填
https://taotoken.net/api,不要带末尾斜杠,也不要在后面拼/v1,具体以页面字段提示为准。填错这一项,后面任务不执行基本就是它。
3. 可复制配置:docker run 与 compose 两种起法
这一节是主线,命令可以直接复制。先拉运行时镜像,OpenHands 的沙箱执行依赖它:
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.54-nikolaik版本标签会随官方更新变化,如果拉取失败,先去官方 README 确认当前标签,别死磕旧版本。
3.1 单条 docker run 启动
docker run -it --rm --pull=always \ -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.54-nikolaik \ -e LOG_ALL_EVENTS=true \ -v /var/run/docker.sock:/var/run/docker.sock \ -v ~/.openhands:/.openhands \ -p 3000:3000 \ --add-host host.docker.internal:host-gateway \ --name openhands-app \ docker.all-hands.dev/all-hands-ai/openhands:0.54几个关键参数拆开说,出问题时你就知道该查哪:
SANDBOX_RUNTIME_CONTAINER_IMAGE:指定沙箱运行时镜像,Agent 执行命令靠它。LOG_ALL_EVENTS=true:打开完整事件日志,排错时非常有用。-v /var/run/docker.sock:/var/run/docker.sock:让容器内能调用宿主机 Docker,这是能力来源,也是风险点。-v ~/.openhands:/.openhands:持久化配置和会话数据。-p 3000:3000:Web GUI 端口映射。--add-host host.docker.internal:host-gateway:方便容器访问宿主机网络。
3.2 docker compose 版本
如果你更习惯 compose,把下面内容存成docker-compose.yml:
services: openhands: image: docker.all-hands.dev/all-hands-ai/openhands:0.54 container_name: openhands-app pull_policy: always environment: SANDBOX_RUNTIME_CONTAINER_IMAGE: docker.all-hands.dev/all-hands-ai/runtime:0.54-nikolaik LOG_ALL_EVENTS: "true" volumes: - /var/run/docker.sock:/var/run/docker.sock - ~/.openhands:/.openhands ports: - "3000:3000" extra_hosts: - "host.docker.internal:host-gateway" stdin_open: true tty: true然后:
docker compose up -d docker compose logs -f3.3 模型通道的配置片段
OpenHands 的模型配置在首次进入 Web 页面时填写,但如果你想把默认配置预置进~/.openhands,可以准备一份 settings 片段。路径和字段名以你当前版本页面为准,下面这份是 OpenAI 兼容通道的通用结构:
{ "llm": { "provider": "openai", "base_url": "https://taotoken.net/api", "api_key": "sk-你的TaoTokenKey", "model": "claude-sonnet-4-5" } }注意:三件套必须同时正确——Base URL、Key、Model ID。少一个或者写错一个,页面能打开但任务不执行。如果你用的是 Claude Code 类接入,Base URL 同样填
https://taotoken.net/api,Key 用同一个,Model ID 换成对应模型即可。
4. 验证请求:从页面打开到任务跑通
容器起来不等于跑通,这一节做三层验证,一层比一层深。
第一层,看容器活着没:
docker ps正常应该看到openhands-app在运行,端口映射是0.0.0.0:3000->3000/tcp。看不到就说明容器压根没起来,回去看日志。
第二层,看日志有没有硬伤:
docker logs -f openhands-app重点扫这几类信息:服务是否正常监听、有没有模型配置报错、有没有 Docker Socket 访问异常。如果日志一直刷错误,页面就算能打开也只是个壳子。
第三层,浏览器访问:
http://localhost:3000远程服务器部署的话,把localhost换成服务器 IP,同时确认安全组和防火墙放行了 3000。
页面打开后,第一次会让你选 Provider、填 Key。走 TaoToken 通道的话,Provider 选 OpenAI 兼容,Base URL 填https://taotoken.net/api,Key 填你生成的,Model ID 填实际模型名。保存后,输入一个最小任务验证:
请帮我创建一个 Python Hello World 示例,并说明如何运行。链路正常的话,你会看到页面里出现步骤性输出:Agent 开始执行、生成文件、可能展示命令执行过程,最后返回结果。如果页面能开但任务一动不动,基本就是模型通道没打通,回去检查三件套。
想单独验证 Key 和模型是否可用,可以先用模型对话页面发一条消息,确认返回正常再回 OpenHands 配置:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
5. 本篇常见报错排查
这一节按真实报错来,遇到哪个查哪个。
报错一:permission denied while trying to connect to the Docker daemon socket
当前用户没权限访问 Docker 守护进程。执行:
sudo usermod -aG docker $USER newgrp docker docker ps还不行就确认 Docker 服务本身在跑:
sudo systemctl status docker sudo systemctl start docker报错二:bind: address already in use
3000 端口被占。先找占用进程:
ss -lntp | grep 3000不想停旧服务就把映射改成 3001,-p 3001:3000,然后访问http://localhost:3001。
报错三:页面能打开,任务不执行
这是最高频的一类,几乎都出在模型通道:Key 填错、Provider 选错、Base URL 写错、Model ID 不存在、或者模型服务本身不可用。按顺序查:Provider 和 Key 是否匹配、Key 是否有效、Base URL 是否是https://taotoken.net/api、Model ID 是否存在、页面报错和容器日志有没有线索。别一遇到就重装容器,重装治不好错误的 Key。
报错四:容器反复退出或启动秒退
先看日志:
docker logs openhands-app因为--rm会让退出后的容器消失,建议先去掉--rm再重启观察。同时检查~/.openhands目录权限、镜像是否拉全:
ls -al ~/.openhands docker images | grep openhands docker images | grep runtime报错五:拉取镜像失败或超时
网络环境问题最常见。切换网络、配置 Docker 镜像加速、稍后重试,并确认镜像地址和标签与官方 README 一致。官方版本号更新了就换新标签,别死磕旧的。
报错六:Docker Socket 相关异常
确认启动参数里有-v /var/run/docker.sock:/var/run/docker.sock,再检查宿主机上文件是否存在:
ls -l /var/run/docker.sock宿主机 Docker 没启动的话,挂进去也没意义。
报错七:OAuth 或认证相关提示
如果你在页面里选了需要 OAuth 的 Provider,但走的是统一 Key 通道,就会卡在认证环节。这种情况直接切回 OpenAI 兼容模式,用 Base URL + Key + Model ID 三件套,不要走 OAuth 流程。
6. 跑通之后:把通道固定下来
到这里,你已经完成了 OpenHands 的一条最小闭环:Docker 起容器、打开 Web GUI、配置模型通道、跑通第一个代码任务、用容器状态和日志验证部署。接下来最值得做的一件事,是把模型通道固定成一套可复用的配置,而不是每次重装都重新填。
如果你打算长期用 OpenHands 做编码任务,建议直接上 Coding Plan,把 Key 和额度统一管理,省得每次换模型都重新配:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
接入文档在这里,字段名和路径以文档为准:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
API Keys 管理页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite
一个实用技巧:把~/.openhands目录定期备份,换机器时直接拷过去,配置和会话都能带走。另一个坑是别把docker.sock挂载到公网可访问的容器里,本地玩没问题,公网部署一定要加反向代理和认证。最后,模型通道的三件套建议写进一个自己的备忘文件,下次重装直接复制,比在页面里凭记忆填靠谱得多。