1. 为什么要把 Codex CLI 塞进 Docker 里跑
Codex CLI 是一个能在终端里直接读写代码、执行命令、跑测试的 coding agent。它最大的价值在于"能动手"——不只是给你建议,而是真的去改文件、装依赖、跑构建。但恰恰是这种能力,让运行边界变得格外重要。我试过直接在宿主机上跑它,结果一次实验性重构把本机的 Node 版本从 20 升到了 22,另一个项目的构建当场挂掉,排查了半小时才反应过来是环境被改了。
所以核心问题不是"Codex 好不好用",而是"让它在哪里用、能看到什么、能改什么"。Docker 隔离运行 Codex CLI 解决的正是这件事:宿主机只保存代码,容器负责跑 Codex 和它需要的一切工具链。Codex 只能看到你明确挂载进去的目录,容器删掉就回到干净状态,本机的 Python、JDK、Node 版本完全不受影响。
这套方案适合几类人:同时维护多个项目的开发者,每个项目的依赖版本不一样;需要分析第三方代码或临时实验的场景,不想让陌生依赖污染本机;以及想把 Codex 接进 CI 或自动化流程的团队,需要可重建、可丢弃的运行环境。
整体结构很清晰:
宿主机 ├── /path/to/my-project # 真实项目代码,唯一暴露给容器的目录 └── Docker └── codex-runner 容器 ├── /workspace # 挂载宿主机项目目录 ├── codex CLI # 容器内安装 ├── node/python/git # 容器内工具链 └── ~/.codex # 配置与登录缓存,可选挂载持久化关键点在于:Codex 的"视野"被限制在/workspace和容器内部,宿主机上其他目录它根本看不到。这比单纯依赖 Codex 自身的 sandbox 更硬——sandbox 是进程级约束,Docker 是文件系统级隔离,两层叠加才稳妥。
接下来我会从镜像构建开始,一步步交付可复制的 Dockerfile、compose 骨架、配置片段,以及接入 TaoToken 统一 Key 通道的完整做法。你跟着敲就能跑起来。
2. TaoToken 统一 Key 接入的前置准备
在动手写 Dockerfile 之前,先把 Key 和通道这件事理清楚,否则后面容器里跑起来会卡在认证上。Codex CLI 支持多种认证方式,但在容器化、自动化场景下,用统一的 API 通道比交互式登录更可控——尤其是当你要在多个项目、多个容器之间复用同一套凭据时。
TaoToken 在这里扮演的角色是统一入口:你拿到一个 Key,配好 Base URL,Codex CLI 就能通过它调用模型,不用在每个容器里单独做浏览器授权。这对 Docker 场景特别友好,因为容器里没有浏览器,设备码登录虽然能用,但每次重建容器都要重来一遍,很烦。
先做三件事。
第一,拿到 API Key。访问 https://taotoken.net/api-keys 创建,注意这个 Key 只在创建时完整显示一次,复制好存到安全的地方。不要写进 Dockerfile,不要提交到 Git,后面我们会用运行时环境变量注入。
第二,确认 Base URL。TaoToken 的 API 端点是https://taotoken.net/api,这个地址在配置 Codex 的config.toml时会用到。注意它不带任何查询参数,就是干净的 API 根路径。
第三,想清楚配置放哪。Codex CLI 读取配置的位置由CODEX_HOME环境变量决定,默认是~/.codex。在容器里,这个目录对应/home/codex/.codex。你有两个选择:一是每次容器启动时通过环境变量注入 Key,配置不持久化;二是把配置目录挂载出来,宿主机上维护一份config.toml,容器复用。我推荐第二种,因为配置集中管理,改一次所有容器都生效。
如果你还没决定用哪种模型,可以先到 https://taotoken.net/models 看看当前可用的模型列表,把 Model ID 记下来,后面写进配置。模型对话页面在 https://taotoken.net/chat,可以用来快速验证 Key 是否有效,不用等容器构建完才发现 Key 有问题。
这里有个容易踩的坑:很多人习惯把 Key 直接写进config.toml然后提交到仓库,这是大忌。正确做法是config.toml里只写非敏感的配置项,Key 通过环境变量OPENAI_API_KEY传入,Codex CLI 会自动读取。这样配置文件可以安全地版本管理,Key 留在运行环境里。
准备好 Key、Base URL、Model ID 这三样,就可以进入镜像构建了。
3. 可复制的 Dockerfile 与 config.toml 配置
这一节是整篇的核心,所有片段都可以直接复制使用。先建目录:
mkdir -p codex-docker-runner cd codex-docker-runner目录结构规划如下:
codex-docker-runner ├── Dockerfile ├── docker-compose.yml ├── config.toml # Codex 配置,挂载进容器 └── codex-home/ # 持久化登录缓存,加入 .gitignore先把codex-home/排除出版本控制:
echo "codex-home/" >> .gitignore3.1 Dockerfile
基于 Ubuntu 24.04,装齐常用工具链,用 npm 安装 Codex CLI,创建非 root 用户避免文件权限混乱:
FROM ubuntu:24.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ git \ bash \ sudo \ python3 \ python3-pip \ python3-venv \ build-essential \ ripgrep \ jq \ vim \ less \ bubblewrap \ && rm -rf /var/lib/apt/lists/* RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ && apt-get update \ && apt-get install -y --no-install-recommends nodejs \ && npm install -g @openai/codex \ && npm cache clean --force \ && rm -rf /var/lib/apt/lists/* RUN useradd -m -s /bin/bash codex \ && echo "codex ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/codex \ && chmod 0440 /etc/sudoers.d/codex USER codex WORKDIR /workspace ENV CODEX_HOME=/home/codex/.codex CMD ["bash"]构建镜像:
docker build -t local/codex-runner:latest .验证 Codex 装好了:
docker run --rm local/codex-runner:latest codex --version3.2 config.toml 配置片段
在codex-docker-runner/下创建config.toml,这是 Codex CLI 读取的配置文件。注意这里不写 Key,Key 走环境变量:
# config.toml model = "gpt-5-codex" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "OPENAI_API_KEY" wire_api = "responses"几个关键字段说明:base_url指向 TaoToken 的 API 根路径;env_key告诉 Codex 从哪个环境变量读 Key,这里用OPENAI_API_KEY;wire_api指定协议类型,按你实际使用的模型和通道要求填写。model字段填你在模型列表里选定的 Model ID。
如果你用的是 Claude Code 风格的接入,配置结构类似,但字段名可能不同,参考 https://taotoken.net/doc 里的对应说明。
3.3 docker-compose.yml 骨架
services: codex-runner: image: local/codex-runner:latest container_name: codex-runner-demo working_dir: /workspace tty: true stdin_open: true volumes: - /Users/you/projects/demo-app:/workspace - ./config.toml:/home/codex/.codex/config.toml:ro - ./codex-home:/home/codex/.codex environment: - TERM=xterm-256color - OPENAI_API_KEY=${OPENAI_API_KEY}注意config.toml用只读挂载(:ro),防止容器内意外修改;codex-home可读写,用于持久化登录状态。OPENAI_API_KEY从宿主机环境变量透传,启动前先export OPENAI_API_KEY=你的Key。
三件套齐了:Base URL 是https://taotoken.net/api,Key 走OPENAI_API_KEY环境变量,Model ID 写在config.toml的model字段。这三样缺一不可,后面排障也围绕它们展开。
4. 容器内验证请求与成功结果
配置写好了,现在启动容器验证整条链路通不通。这一步很关键,因为 Docker 网络、环境变量透传、配置文件挂载任何一个环节出问题,都会在调用模型时才暴露。
先导出 Key:
export OPENAI_API_KEY=你的TaoTokenKey启动容器:
docker compose run --rm codex-runner进入容器后,先确认环境:
pwd ls codex --version echo $OPENAI_API_KEY | head -c 8pwd应该输出/workspace,ls能看到你挂载的项目文件,codex --version打印版本号,最后一行确认 Key 已经透传进来(只显示前 8 位,避免泄露)。
接着验证配置文件被正确读取:
cat /home/codex/.codex/config.toml应该能看到你写的base_url和model字段。
现在做一次最小化的模型调用验证。在容器里直接跑:
codex exec "用一句话说明当前目录下有哪些文件"如果一切正常,Codex 会读取/workspace下的文件列表并返回描述。这一步成功意味着:容器网络能访问 TaoToken API、Key 认证通过、模型 ID 有效、配置文件解析正确。
你也可以用更直接的方式验证 API 通道,在容器里用 curl 打一次:
curl -s https://taotoken.net/api/models \ -H "Authorization: Bearer $OPENAI_API_KEY" | jq '.data[].id' | head返回模型列表说明 Key 和网络都没问题。如果这一步失败但codex exec能跑,那问题在 Codex 的配置解析;如果两步都失败,问题在 Key 或网络。
验证通过后,日常使用就简单了。写一个run-codex.sh脚本:
#!/usr/bin/env bash set -euo pipefail PROJECT_DIR="${1:-$PWD}" CONTAINER_NAME="codex-runner-$(basename "$PROJECT_DIR")" docker run --rm -it \ --name "$CONTAINER_NAME" \ -v "$PROJECT_DIR":/workspace \ -v "$(pwd)/config.toml":/home/codex/.codex/config.toml:ro \ -v "$HOME/.codex-docker-home":/home/codex/.codex \ -e OPENAI_API_KEY \ -w /workspace \ local/codex-runner:latest \ codex --sandbox workspace-write --ask-for-approval on-request赋权后使用:
chmod +x run-codex.sh ./run-codex.sh /path/to/your-project这样每次针对不同项目启动独立容器,Codex 只在挂载的项目目录里工作,宿主机其他部分完全隔离。实测下来,从启动到 Codex 开始响应通常在几秒内,比每次配置本机环境快得多。
5. 常见报错排查:401、local proxy failed、reading choices
这一节对照真实报错,把最容易卡住的几个问题拆开讲。这些错误我在不同阶段都遇到过,按顺序排查基本能定位。
5.1 401 Unauthorized
最常见,表现为codex exec返回 401 或invalid api key。原因通常是 Key 没透传进容器。检查顺序:
先在宿主机确认环境变量存在:
echo $OPENAI_API_KEY | head -c 8如果宿主机就是空的,说明export没执行或写在了错误的 shell 会话里。docker compose的environment段里写的是${OPENAI_API_KEY},它从宿主机当前 shell 读取,所以必须在同一个终端里先 export。
如果宿主机有值但容器里没有,检查 compose 文件里environment段是否正确引用了变量名,以及docker compose run时有没有加-e覆盖。用docker compose run --rm codex-runner env | grep OPENAI确认容器内环境变量。
还有一种情况:Key 本身失效或额度用尽。到 https://taotoken.net/api-keys 确认 Key 状态,或者用 https://taotoken.net/chat 快速测一下这个 Key 能不能正常对话。
5.2 local proxy failed / connection refused
报错类似local proxy failed或dial tcp: connection refused,说明容器内访问不到 TaoToken 的 API 端点。先确认容器网络:
docker run --rm local/codex-runner:latest curl -sI https://taotoken.net/api如果这条命令超时或拒绝连接,检查宿主机的 Docker 网络配置,以及是否有防火墙规则拦截了容器出站流量。企业内网环境可能需要配置 Docker 的 DNS 或 HTTP 代理,这部分按你所在网络的规范处理。
如果 curl 能通但 Codex 报 proxy failed,检查config.toml里的base_url是否写成了带路径的形式。正确写法是https://taotoken.net/api,不要多加/v1或其他后缀,具体路径由 Codex 根据wire_api自动拼接。
5.3 reading choices / 响应解析失败
报错包含reading choices或unexpected response format,通常是wire_api字段和实际通道不匹配。Codex CLI 支持responses和chat两种协议,TaoToken 通道用哪种取决于你选的模型。到 https://taotoken.net/doc 查对应模型的接入说明,把config.toml里的wire_api改成正确的值。
另一个可能是 Model ID 写错了。model字段必须和 TaoToken 模型列表里的 ID 完全一致,大小写敏感。用前面 curl 模型列表的命令确认准确的 ID。
5.4 OAuth / 登录相关报错
如果你选择交互式登录而不是 API Key,容器里跑codex login可能报 OAuth 回调失败,因为容器没有浏览器。改用设备码方式:
codex login --device-auth终端会显示一个码和 URL,在宿主机浏览器里打开完成授权。授权状态存在CODEX_HOME里,如果你挂载了codex-home,重建容器后不用重新登录。
但要注意:codex-home里可能包含auth.json等敏感凭据,绝对不要提交到 Git。如果团队协作,建议统一用 API Key 方式,凭据通过环境变量或 secret 管理,不落地到文件。
5.5 sandbox 相关报错
容器里跑 Codex 时可能遇到bwrap: operation not permitted或 sandbox 初始化失败。这是因为 Docker 默认的 seccomp 配置限制了 bubblewrap 需要的 namespace 操作。两个选择:一是给容器加--security-opt seccomp=unconfined和--cap-add SYS_ADMIN,让内层 sandbox 能工作;二是承认 Docker 本身就是隔离边界,在容器内用codex --sandbox danger-full-access,但前提是挂载范围足够小,只挂项目目录。
我倾向第二种,因为 Docker 的文件系统隔离已经比 Codex 内层 sandbox 更硬,没必要为了内层 sandbox 放宽容器的安全配置。但如果你挂载了 Docker socket 或 SSH key,那隔离意义就大打折扣了,这种情况必须保留内层 sandbox。
6. 长期编码与 Agent 场景的接入建议
如果你只是偶尔用 Codex 做一次性任务,前面这套 Docker 方案已经够用。但如果你打算把 Codex 作为日常编码助手,或者接进自动化流程长期跑,有几个点值得提前规划。
第一是凭据管理。API Key 通过环境变量注入适合本地开发,但在 CI 或服务器上,建议用 secret 管理工具,不要把 Key 写进任何会进版本控制的文件。config.toml可以安全提交,因为它只包含 Base URL 和 Model ID,不含敏感信息。
第二是配置复用。把config.toml和run-codex.sh放在一个独立的 runner 仓库里,所有项目共用。每个项目只需要在启动时指定路径,不用重复配置。这样升级模型或切换通道时,改一处就够。
第三是 Coding Plan 场景。如果你需要长时间、多轮次的 Agent 编码任务,TaoToken 的 Coding Plan 提供了更适合持续调用的通道方案,具体可以到 https://taotoken.net/coding-plan 了解。它和按次调用的 API Key 是互补的,前者适合交互式探索,后者适合稳定的批量任务。
第四是 Claude Code 风格的接入。如果你同时用 Claude Code 和 Codex,两者的配置结构不同但思路一致:都是 Base URL + Key + Model ID 三件套。Claude Code 的配置参考 https://taotoken.net/claude-code-anthropic,Codex 的配置就是本文的config.toml。统一用 TaoToken 作为通道,好处是 Key 和模型管理集中在一处,不用为每个工具单独申请凭据。
最后说一个实际经验:容器化运行 Codex 最大的收益不是"安全",而是"可重建"。本机环境跑久了总会积累各种临时改动,出问题很难回到干净状态。容器删掉重建只要几秒,而且每次都是确定性的环境。对于需要反复实验、分析陌生代码、跑一次性任务的场景,这种确定性比什么都值钱。
日常使用中,我建议从最简单的docker run -v 当前项目:/workspace开始,跑顺了再补 compose、脚本和网络限制。不要一上来就追求完美配置,先把链路跑通,再逐步收紧边界。