1. 为什么我最后把 OpenClaw 塞进了一个 DockerfileOpenClaw小龙虾是一个可以自己部署的 AI 助手网关能接兼容 OpenAI 协议的模型服务然后在浏览器里对话、跑 Agent、挂 Telegram 通道。它适合两类人一类是想有个自己的 AI 控制台、不想被某个客户端绑死另一类是手里已经有模型 API想找个统一入口把对话、编码、自动化都接进去。问题出在部署环节。Hugging Face Spaces 的免费 CPU Basic 实例给 2 vCPU、16 GB 内存、100 GB 磁盘听起来够用但它有两个坑一是容器重启后本地文件全丢二是免费实例对部分外部域名的解析不稳定。我一开始按官方思路拆了sync.py、start-openclaw.sh、requirements.txt、Nginx 配置四五个文件结果每次改一个环境变量就要动三处备份恢复还经常对不上。后来我把所有逻辑压进一个 Dockerfile装依赖、生成同步脚本、生成启动脚本、修 DNS、恢复备份、定时备份、拉起 gateway全在构建和启动阶段自动完成。你只需要在 Space 仓库根目录放一个Dockerfile再配几个环境变量就能跑起来。这篇就把这个单文件方案完整拆给你包括怎么把模型请求统一接到 TaoToken 的 Key/API 通道上。2. 部署前先把 TaoToken 的接入信息准备好OpenClaw 本身不带模型它需要一个兼容 OpenAI 协议的服务地址、一个 API Key、一个模型 ID。我这边统一走 TaoToken 的 API 通道好处是后面换模型只改一个MODEL变量不用动 Dockerfile。你需要提前拿到三样东西API Basehttps://taotoken.net/apiAPI Key在控制台里生成形如sk-开头的一串模型 ID填你实际要用的那个比如某个对话模型或编码模型的名字生成 Key 的入口在这里登录后进 API Keys 页面新建即可https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdockerfile_openclaw如果你还没想好接哪个模型可以先在模型对话页面试一条请求确认 Key 能用、模型名没写错再回来配 Spacehttps://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdockerfile_openclaw接口文档在这里主要看 base URL 和鉴权头的写法OpenClaw 内部就是按这套协议发请求的https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdockerfile_openclaw这里有个容易踩的点OPENAI_API_BASE到底填https://taotoken.net/api还是带/v1。OpenClaw 的配置里会把它当作 OpenAI 兼容根地址实际请求路径是它自己拼的。我实测下来填https://taotoken.net/api就能通如果你填了带/v1的版本反而可能拼出双/v1。拿不准就先按不带/v1的填报 404 再调。3. 单个 Dockerfile 的完整骨架下面这份就是我要你复制进 Space 仓库根目录的内容。它做了几件事基于node:22-slim装 Node、Python、Git、编译工具和网络诊断工具装huggingface_hub用于备份同步全局装openclawlatest在镜像里生成sync.py和start-openclaw两个脚本启动时覆盖 DNS、从 Dataset 恢复最近备份、生成初始配置、每 15 分钟全量备份、收到终止信号再备份一次。FROM node:22-slim ENV DEBIAN_FRONTENDnoninteractive ENV HOME/root ENV PORT7860 RUN apt-get update apt-get install -y --no-install-recommends \ python3 python3-pip git openssh-client build-essential \ curl dnsutils iputils-ping ca-certificates \ rm -rf /var/lib/apt/lists/* RUN pip3 install --no-cache-dir --break-system-packages huggingface_hub RUN npm install -g openclawlatest # 内嵌备份/恢复脚本 RUN cat /usr/local/bin/sync.py PY import os, sys, glob, tarfile, datetime from huggingface_hub import HfApi, hf_hub_download STATE_DIR /root/.openclaw DATASET os.environ.get(HF_DATASET, ) TOKEN os.environ.get(HF_TOKEN, ) KEEP_DAYS 30 def backup(): if not DATASET or not TOKEN: print(--- [SYNC] 缺少 HF_DATASET 或 HF_TOKEN跳过备份) return api HfApi(tokenTOKEN) stamp datetime.date.today().isoformat() name fbackup_{stamp}.tar.gz tmp f/tmp/{name} with tarfile.open(tmp, w:gz) as tar: if os.path.isdir(STATE_DIR): tar.add(STATE_DIR, arcname.openclaw) api.upload_file(path_or_fileobjtmp, path_in_reponame, repo_idDATASET, repo_typedataset) print(f--- [SYNC] 备份完成: {name}) def restore(): if not DATASET or not TOKEN: print(--- [SYNC] 缺少 HF_DATASET 或 HF_TOKEN跳过恢复) return api HfApi(tokenTOKEN) files api.list_repo_files(repo_idDATASET, repo_typedataset) backups sorted([f for f in files if f.startswith(backup_) and f.endswith(.tar.gz)]) if not backups: print(--- [SYNC] 未发现备份文件按首次运行处理) return latest backups[-1] print(f--- [SYNC] 发现备份文件: {latest}) path hf_hub_download(repo_idDATASET, filenamelatest, repo_typedataset, tokenTOKEN) try: with tarfile.open(path, r:gz) as tar: tar.extractall(/root) print(--- [SYNC] 恢复成功) except Exception as e: print(f--- [SYNC] 解压失败: {e}) if __name__ __main__: if sys.argv[1] backup: backup() elif sys.argv[1] restore: restore() PY # 内嵌启动脚本 RUN cat /usr/local/bin/start-openclaw SH #!/bin/bash set -e echo --- [DNS] 覆盖 DNS 配置 --- echo nameserver 223.5.5.5 /etc/resolv.conf echo nameserver 8.8.8.8 /etc/resolv.conf echo --- [SYNC] 启动恢复流程 --- python3 /usr/local/bin/sync.py restore || true if [ -z $OPENAI_API_KEY ]; then echo OPENAI_API_KEY is required; exit 1 fi if [ -z $MODEL ]; then echo MODEL is required; exit 1 fi CONF/root/.openclaw/openclaw.json mkdir -p /root/.openclaw if [ ! -f $CONF ]; then echo --- [INIT] 生成初始配置 --- if [ -z $OPENCLAW_GATEWAY_PASSWORD ]; then OPENCLAW_GATEWAY_PASSWORD$(head -c 12 /dev/urandom | base64 | tr -d /) echo --- [INIT] Gateway 密码: $OPENCLAW_GATEWAY_PASSWORD fi echo $OPENCLAW_GATEWAY_PASSWORD /root/.openclaw/gateway.passwd cat $CONF JSON { gateway: { password: $OPENCLAW_GATEWAY_PASSWORD }, models: { default: { baseUrl: $OPENAI_API_BASE, apiKey: $OPENAI_API_KEY, model: $MODEL } } } JSON fi # 后台定时备份 ( while true; do sleep 900; python3 /usr/local/bin/sync.py backup || true; done ) trap python3 /usr/local/bin/sync.py backup || true TERM INT openclaw doctor --fix || true exec openclaw gateway run --port $PORT SH RUN chmod x /usr/local/bin/start-openclaw EXPOSE 7860 CMD [/usr/local/bin/start-openclaw]这份文件里没有单独的requirements.txtPython 依赖直接pip3 install没有单独的 Nginx 配置OpenClaw 自己监听7860没有单独的sync.py文件它是在构建阶段用 heredoc 写进镜像的。你维护的永远只有这一个 Dockerfile。4. Space 环境变量与 settings.json 接入片段Dockerfile 提交后去 Space 的 Settings 页面在 Variables and secrets 里加下面这些。敏感值放 Secrets非敏感的放 Variables。变量名是否必填说明OPENAI_API_BASE必填填https://taotoken.net/apiOPENAI_API_KEY必填TaoToken 控制台生成的 Key放 SecretsMODEL必填你要用的模型 IDHF_DATASET必填备份用的 Dataset格式用户名/数据集名HF_TOKEN必填Hugging Face Write Token放 SecretsOPENCLAW_GATEWAY_PASSWORD建议填登录 OpenClaw 页面的密码PORT可选默认 7860一般不用改TELEGRAM_BOT_TOKEN可选不用 Telegram 就别填如果你不想用 Dockerfile 里自动生成的openclaw.json也可以手动写一份配置挂进去。OpenClaw 读的是/root/.openclaw/openclaw.json接入 TaoToken 的关键片段长这样{ gateway: { password: 你的登录密码 }, models: { default: { baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, model: 你的模型ID } } }注意baseUrl这里我填的是不带/v1的版本。如果你手动改配置后发现请求 404先把baseUrl换成带/v1的再试一次两种写法在不同 OpenClaw 版本里行为略有差异。改完配置记得重启 Space因为启动脚本只在openclaw.json不存在时才生成初始配置已存在的文件它不会覆盖。5. 验证部署发一次真实对话请求构建完成后打开 Space 的 App 页面地址形如https://你的空间名.hf.space。输入OPENCLAW_GATEWAY_PASSWORD登录。如果你没设密码去 Logs 里搜--- [INIT] Gateway 密码:那串就是。登录后别急着配一堆东西先做一次最小验证在对话界面发一句「你好用一句话介绍你自己」。这一步会真正打到 TaoToken 的接口上能返回内容就说明baseUrl、apiKey、model三个值都对。如果你想在容器里直接验证可以进 Space 的终端跑一条 curlcurl -s https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $OPENAI_API_KEY \ -H Content-Type: application/json \ -d { model: $MODEL, messages: [{role: user, content: ping}] }返回里带choices字段就说明 Key 和模型 ID 没问题。如果这里通、OpenClaw 页面不通那问题在 OpenClaw 的配置读取上不在网络。验证完对话再做一次备份闭环测试随便改一个配置或建一个会话等 15 分钟让定时备份跑一次然后去 Dataset 里看有没有backup_日期.tar.gz。有文件就说明HF_DATASET和HF_TOKEN配对了。接着手动重启 Space重新登录看之前的会话还在不在。在就说明恢复流程也通了。6. 部署后常见报错排查构建阶段npm install -g openclawlatest失败。多半是网络波动先重新触发一次构建。如果稳定复现检查 Dockerfile 里FROM node:22-slim这行有没有被误改以及apt-get update那一段有没有语法错误。构建日志会明确告诉你卡在哪一层。页面打不开日志里没有 gateway 启动信息。先看日志有没有OPENAI_API_KEY is required或MODEL is required。启动脚本在这两个变量缺失时会直接退出不会拉起 gateway。补齐变量后重启 Space再确认日志里出现openclaw gateway run --port。能打开页面但登录不进去。如果你手动设了密码确认输入值和 Secrets 里一致。如果没设去日志找--- [INIT] Gateway 密码:。首次启动后这个密码会写进/root/.openclaw/gateway.passwd并随备份保存。如果重启后密码变了说明备份没恢复成功按下面那条处理。重启后配置或会话丢失。先去 Dataset 看最近日期的backup_YYYY-MM-DD.tar.gz在不在。不在说明定时备份没写进去检查HF_DATASET和HF_TOKEN尤其确认 Token 是 Write 权限。文件在但没恢复看启动日志里有没有--- [SYNC] 发现备份文件和--- [SYNC] 恢复成功根据报错定位。模型调用失败。检查OPENAI_API_BASE、OPENAI_API_KEY、MODEL三个值。如果你改了环境变量但没删旧配置OpenClaw 会继续用旧的openclaw.json。确认要换配置的话先备份当前状态删掉/root/.openclaw/openclaw.json再重启让脚本重新生成。免费实例休眠导致掉线。这是 Hugging Face 免费层的机制不是部署问题。你可以加一个定时轮询请求保持活跃或者接受它偶尔休眠、靠备份恢复。要真正稳定在线得升级硬件或换到常驻环境。7. 接下来怎么把这套东西用顺部署跑通之后我建议先把MODEL固定下来别频繁换因为每次换都要重启 Space 重新生成配置。备份频率现在是 15 分钟一次、保留 30 天如果你会话不多可以调长一点减少 Dataset 写入。如果你后面要长期跑编码任务或 Agent可以看看 Coding Plan它更适合持续性的模型调用场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdockerfile_openclaw需要管理多个 Key 或看调用量去控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdockerfile_openclaw这套单文件方案的价值在于你只维护一个 Dockerfile状态靠 Dataset 持久化模型接入靠三个环境变量。等你把基础对话跑顺了再往上加 Telegram 通道或远程访问改动都集中在这一个文件里不会像多文件方案那样牵一发动全身。