1. Docker 里跑 Codex CLI 的配置与升级痛点Codex CLI 这类终端编码助手更新节奏很快几乎每隔一两周就有新版本。如果你把它直接装在宿主机上升级就是一条 npm 命令的事。但一旦放进 Docker 容器事情立刻变得复杂容器一重建auth.json没了、config.toml没了、Base URL 和 Key 全都要重新填一遍。更麻烦的是很多人把 Codex CLI 和科研环境、Python 依赖、ROOT、Geant4 这些重型基础镜像塞在同一个 Dockerfile 里结果每次升级 Codex 都要重建一个几个 GB 的大镜像耗时又容易出错。这篇要解决的就是这个场景在 Docker 容器内为 Codex CLI 做隔离式配置与版本升级让认证状态、Base URL、Key 这些长期数据独立于程序本体让程序本体独立于稳定的基础环境。核心思路是把三样东西拆开管理——稳定的基础环境、高频更新的 Codex CLI 程序、长期保存的用户配置。拆开之后升级 Codex 只动程序层配置层和基础层都不受影响容器重建后auth.json依然在Key 和 endpoint 也不用重填。适合谁看用 Docker 做开发环境、又想把 Codex CLI 接进容器里长期用的同学尤其是那些基础镜像很大、重建一次要等好几分钟的场景。下面我会给出可复制的 Dockerfile 片段、挂载目录结构、auth.json示例并演示把 endpoint 改到 TaoToken 后跑一次 CLI 调用验证连通性。整个过程你可以跟着做最后会得到一个「配置与升级互不干扰、可重复构建」的容器结构。先说清楚为什么要用 TaoToken 统一 Key。Codex CLI 默认走官方 endpoint但很多团队希望把请求收敛到一个可控的入口方便统一管理 Key、切换模型、看用量。TaoToken 提供兼容的 API 入口把 Base URL 指过去、Key 换成 TaoToken 的 Key就能在容器里用同一套配置跑起来。官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数配置里填干净的就行。2. 前置准备TaoToken Key 与目录分层设计动手之前先把两件事准备好一个可用的 TaoToken Key以及一套清晰的目录分层。Key 的获取走控制台登录后在 API Keys 页面创建即可地址是 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建后复制那串sk-开头的字符串后面写进auth.json。如果你还没决定用哪个模型可以先去模型对话页面看看有哪些可选地址是 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 确认好 Model ID 再往下走。目录分层是整个方案的骨架。我把它分成三层对应三个 Docker 概念第一层是基础环境层比如research-dev-base里面装 Python、ROOT、Geant4 这些不常变的东西。这一层构建一次可以用很久不随 Codex 版本更新。第二层是程序层叫codex-runtime专门放 Codex CLI 本体。它是个独立镜像或独立卷升级 Codex 就是更新这一层。工作容器以只读方式挂载它保证运行时不会被意外改写。第三层是配置层叫codex-profile保存config.toml、auth.json、登录状态这些长期数据。它以读写方式挂载进工作容器容器重建也不丢。运行关系可以这样理解工作容器wiki-dev从基础环境层继承系统依赖只读挂载程序层的/opt/codex读写挂载配置层的/root/.codex。升级时另起一个codex-updater容器只对程序层做读写更新。这样基础层和配置层在升级过程中完全不被触碰。宿主机上的目录结构建议这样组织以 Windows 为例Linux/macOS 把盘符换成对应路径即可wiki-docker-runtime/ ├── .env # 存放 CODEX_VERSION 等变量 ├── docker-compose.yml ├── base/ │ └── Dockerfile # research-dev-base ├── runtime/ │ └── Dockerfile # codex-runtime └── profile/ # 挂载到容器的 /root/.codex ├── auth.json └── config.toml.env里放版本号这是升级的唯一开关CODEX_VERSION0.9.0 TAOTOKEN_BASE_URLhttps://taotoken.net/api把版本号抽到.env的好处是升级时只改一行不用碰 Dockerfile。TAOTOKEN_BASE_URL也放这里方便统一改 endpoint。注意这里填的是https://taotoken.net/api不带任何查询参数。配置层里的auth.json是认证核心格式大致如下把OPENAI_API_KEY换成你自己的 TaoToken Key{ OPENAI_API_KEY: sk-你的TaoTokenKey, OPENAI_BASE_URL: https://taotoken.net/api }config.toml里可以指定默认模型Model ID 用你在模型对话页面确认过的那个model gpt-5-codex provider openai这里有个容易踩的坑auth.json和config.toml的字段名会随 Codex CLI 版本变化。升级后如果报认证相关错误先对照新版本的文档确认字段名别急着怀疑 Key 失效。我试过在版本跳跃较大时OPENAI_BASE_URL被要求改叫别的名字结果排查了半天才发现是字段名变了。3. 可复制配置Dockerfile 与 compose 片段这一节给出可以直接抄的配置。先看程序层的 Dockerfile它的职责只有一个把指定版本的 Codex CLI 装进/opt/codex。# runtime/Dockerfile FROM node:20-slim AS codex-runtime ARG CODEX_VERSION0.9.0 RUN npm install -g openai/codex${CODEX_VERSION} \ mkdir -p /opt/codex \ cp -r $(npm root -g)/openai/codex /opt/codex/ ENV PATH/opt/codex/bin:${PATH}这里用ARG CODEX_VERSION接收构建参数npm install -g装完后把包复制到/opt/codex这样工作容器挂载这个目录就能直接用。ENV PATH保证codex命令可执行。再看基础层的 Dockerfile它不关心 Codex只装科研环境# base/Dockerfile FROM ubuntu:22.04 RUN apt-get update apt-get install -y \ python3 python3-pip git curl \ rm -rf /var/lib/apt/lists/* # 这里继续装 ROOT、Geant4 等重型依赖 # 它们不随 Codex 版本变化构建一次长期复用关键是基础层里不要出现任何 Codex 相关内容。这样升级 Codex 时这个几个 GB 的镜像完全不用重建。接下来是docker-compose.yml它把三层串起来并定义 updater 服务services: wiki-dev: build: context: ./base image: research-dev-base:latest volumes: - ./runtime/codex:/opt/codex:ro - ./profile:/root/.codex:rw - ./workspace:/workspace environment: - OPENAI_BASE_URL${TAOTOKEN_BASE_URL} working_dir: /workspace command: sleep infinity codex-updater: build: context: ./runtime args: CODEX_VERSION: ${CODEX_VERSION} image: codex-runtime:${CODEX_VERSION} profiles: [updater] volumes: - ./runtime/codex:/opt/codex:rw注意几个细节。wiki-dev挂载/opt/codex时用:ro只读防止运行时误改程序。挂载/root/.codex用:rw因为 Codex 运行时会写登录状态和缓存。codex-updater用profiles: [updater]隔离平时docker compose up不会启动它只有显式指定 profile 才跑。updater 挂载/opt/codex用:rw因为它的任务就是写这个目录。OPENAI_BASE_URL通过环境变量注入值来自.env里的TAOTOKEN_BASE_URL。这样即使auth.json里没写 Base URL环境变量也能兜底。不过建议两处都写避免不同版本读取优先级不一致导致的问题。如果你用的是 Cline MCP 或 Claude Code 这类工具配置思路一样三件套要写全Base URL 填https://taotoken.net/apiKey 填 TaoToken 的 KeyModel ID 填你在模型对话页面确认的值。缺任何一个都会连不上。Codex CLI 的auth.json里 Base URL 和 Key 是必须的Model ID 在config.toml里指定。构建顺序是先建基础层再建程序层docker compose build wiki-dev docker compose --profile updater build codex-updater第一次构建基础层会比较慢因为要装重型依赖。之后升级 Codex 只需要重建codex-updater几秒钟就完成。4. 验证请求改 endpoint 后跑一次 CLI 调用配置写好后最重要的一步是验证连通性。先确认容器能起来并且 Codex 命令可用docker compose up -d wiki-dev docker compose exec wiki-dev codex --version如果输出版本号说明程序层挂载成功。如果报command not found检查/opt/codex/bin是否在 PATH 里以及挂载路径是否对得上。接着验证认证和 endpoint。先看环境变量有没有正确注入docker compose exec wiki-dev env | grep OPENAI应该能看到OPENAI_BASE_URLhttps://taotoken.net/api。然后确认auth.json在容器里可读docker compose exec wiki-dev cat /root/.codex/auth.json输出的 JSON 里OPENAI_API_KEY应该是你的 TaoToken KeyOPENAI_BASE_URL是 TaoToken 的 API 地址。现在跑一次真实调用。Codex CLI 的交互模式在容器里不好演示用非交互的方式发一个简单请求docker compose exec wiki-dev codex exec print hello from docker这条命令会让 Codex 处理一个简单任务。如果配置正确你会看到它返回结果说明请求成功打到了 TaoToken 的 endpoint。如果卡住或报错看下一节的排查清单。想更直接地验证 endpoint 通不通可以用 curl 打一次 APIdocker compose exec wiki-dev curl -s https://taotoken.net/api/v1/models \ -H Authorization: Bearer $OPENAI_API_KEY | head -c 300返回模型列表的 JSON 片段就说明 Key 和 Base URL 都没问题。这一步能把「网络问题」和「Codex 配置问题」分开排查时很有用。验证通过后测试升级流程是否真的不影响配置。改.env里的版本号# Linux/macOS sed -i s/CODEX_VERSION.*/CODEX_VERSION0.9.1/ .env # Windows PowerShell (Get-Content .env) -replace CODEX_VERSION.*, CODEX_VERSION0.9.1 | Set-Content .env然后跑 updaterdocker compose --profile updater run --rm codex-updaterupdater 会把新版本写进./runtime/codex。重启工作容器让它重新挂载docker compose restart wiki-dev docker compose exec wiki-dev codex --version版本号应该变成新的。同时再cat一次auth.json内容原封不动——这就是分层设计要达到的效果升级程序配置不动。确认 compose 读到的版本号也对docker compose --profile updater config | grep CODEX_VERSION输出里应该显示你刚改的新版本。如果这里显示的还是旧值说明.env没被正确读取检查文件编码和路径。5. 常见报错排查401、local proxy failed 与 OAuth配置过程中最容易撞上几类错误逐个说清楚怎么定位。401 Unauthorized。这是认证失败最常见的原因是 Key 不对或没生效。先确认auth.json里的 Key 和 TaoToken 控制台里的一致注意别把前后空格复制进去。然后确认环境变量没有覆盖掉文件里的值——如果OPENAI_API_KEY环境变量存在且为空可能反而把文件里的好 Key 顶掉了。排查命令docker compose exec wiki-dev env | grep OPENAI_API_KEY docker compose exec wiki-dev cat /root/.codex/auth.json两处对比以你期望的为准。如果环境变量是空的从 compose 里删掉那行只靠auth.json。local proxy failed / connection refused。这类错误说明请求根本没出去或者 Base URL 写错了。检查OPENAI_BASE_URL是不是https://taotoken.net/api注意不要多写/v1或少写/api路径不对会直接 404 或连接失败。另外确认容器有外网访问能力curl那条命令能返回结果就说明网络没问题。如果 curl 通但 Codex 不通问题在 Codex 的配置读取上不在网络。reading choices 相关报错。这通常出现在响应解析阶段说明请求发出去了、也收到了响应但返回结构不是 Codex 预期的格式。常见原因是 Model ID 填错或者 endpoint 指向了一个不兼容的路径。回到config.toml确认model字段用的是模型对话页面里列出的有效 ID。如果刚升级过 Codex也可能是新版本对响应格式要求变了对照版本说明确认。OAuth 登录相关报错。Codex CLI 某些版本会尝试走 OAuth 登录流程在容器里没有浏览器这个流程会失败。解决办法是确保auth.json里已经有有效的 API Key让 CLI 走 Key 认证而不是 OAuth。如果它仍然强制走 OAuth检查版本是否支持纯 Key 模式必要时在config.toml里显式指定认证方式。容器里不要指望能弹出浏览器完成登录所有认证都要靠预置的auth.json。升级后命令找不到。codex --version报command not found多半是 updater 没跑成功或者挂载路径变了。先确认./runtime/codex目录里有内容ls -la ./runtime/codex/bin如果目录是空的重跑 updater 并看它的构建日志有没有报错。如果目录有内容但容器里找不到检查 compose 里的挂载路径和PATH环境变量是否匹配。配置改了但容器里没生效。Docker 的挂载是启动时确定的改了宿主机上的auth.json后容器里看到的是同一份文件因为是 bind mount一般会立即生效。但如果改的是 compose 里的环境变量必须docker compose up -d重建容器才生效restart不够。这个区别要记清楚。排查时有个通用思路先用curl验证网络和 Key再用codex exec验证 CLI 配置最后用codex --version验证程序层。三层分开测问题出在哪一层一目了然。6. 把 Key 和 endpoint 收敛到 TaoToken 的长期做法走到这里你应该已经有一个能跑、能升级、配置不丢的容器结构了。最后说几个长期维护上的实用点。升级流程固定成三步改.env里的CODEX_VERSION跑docker compose --profile updater run --rm codex-updater然后docker compose restart wiki-dev。这三步可以写成一个脚本以后升级就是执行一次脚本的事。基础层镜像完全不用重建省下大量等待时间。Key 的管理建议收敛到 TaoToken 一处。所有容器、所有工具都从同一个入口拿 KeyBase URL 统一填https://taotoken.net/api。这样换 Key 或轮换时只改一个地方不用满仓库找配置。如果你同时用 Codex CLI 和其他编码工具长期跑 Agent 任务的话可以看看 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 把额度集中管理。接入细节和字段说明在文档里地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到字段名变化时以文档为准。配置层目录./profile建议纳入版本控制但auth.json里的真实 Key 不要提交用占位符或.gitignore排除。这样换机器时把目录拷过去改一下 Key 就能用环境完全一致。还有一个细节codex-runtime镜像的 tag 用版本号比如codex-runtime:0.9.1。这样回滚时只要把.env里的版本号改回旧值、重跑 updater 就行不用重新构建。镜像层有缓存回滚也很快。最后提醒一句容器里的 Codex CLI 和宿主机上的可以共存互不影响。容器方案的价值在于环境隔离和可重复构建代价是多一层挂载配置。一旦这套结构搭好后面升级就是改一行版本号的事配置和认证状态始终稳稳地待在codex-profile里。