1. 这个报错到底在说什么exec: codex: executable file not found in $PATH这句话拆开看其实很直白容器运行时runc准备启动进程拿着codex这个名字去$PATH列出的目录里挨个找可执行文件找了一圈没找到于是直接拒绝创建容器进程。注意它报的是exec阶段失败不是 Codex CLI 自己运行时报错也就是说 Codex 根本没被拉起来问题出在镜像里“有没有这个命令”以及“PATH 认不认这个目录”。我遇到这个问题的场景很典型本地用npm install -g openai/codex装完命令行敲codex一切正常于是信心满满写进 Dockerfile结果docker run直接给你一记executable file not found。更迷惑的是有时候docker exec -it container sh进去手动敲codex又能找到有时候又找不到。这种“时灵时不灵”基本可以锁定两类根因一是多阶段构建里安装层和运行层不是同一个镜像产物没被拷过去二是 PATH 环境变量没把 npm 全局 bin 目录包含进来。这篇就按“先定位、再修复、后验证”的顺序走一遍覆盖 PATH 环境变量、镜像构建层、入口脚本三类根因最后给一套通过 TaoToken 统一 Key/API 通道接入时的config.toml骨架和容器内验证命令。适合正在把 Codex CLI 塞进 Docker 做本地开发或 CI 自动化的同学跟着敲一遍就能复现并确认修复生效。2. 先把 TaoToken 的接入前置准备好在动 Dockerfile 之前建议先把模型通道这层理顺否则你修好了codex找不到的问题容器起来了又卡在鉴权或 base_url 上排查会互相干扰。TaoToken 在这里的角色是统一 Key 和 API 通道你不需要在容器里散落多个厂商的 Key而是用一个统一入口把模型对话、编码类请求都收敛到同一套配置里。具体做法是先拿到一个可用的 API Key然后确认你要用的接入地址。TaoToken 的 API 入口是https://taotoken.net/api官网是https://taotoken.net/。如果你只是想让 Codex CLI 在容器里能正常对话和补全用 API Key 就够了如果你打算长期跑编码任务或者接 Agent 工作流可以顺带了解下 Coding Plan把额度模型固定下来避免 CI 里跑着跑着额度见底。拿 Key 的路径不复杂进控制台在 API Keys 页面创建一个新 Key复制出来先存到本地环境变量里别直接写进 Dockerfile。容器里通过-e或 compose 的environment注入这样镜像本身不含密钥推到私有仓库也安全。这一步做完你手里应该有一个TAOTOKEN_API_KEY和一个明确的 base_url接下来所有配置都围绕这两个值展开。3. 可复制的 Dockerfile 与 compose 配置3.1 单阶段直接安装最省心如果你的项目不需要多阶段构建做体积优化最稳的方式就是在最终运行镜像里直接装 Codex CLI并且显式补 PATHFROM node:20-slim # 安装 Codex CLI RUN npm install -g openai/codex # 显式把 npm 全局 bin 目录放进 PATH避免精简镜像 PATH 缺失 ENV PATH/usr/local/bin:${PATH} WORKDIR /workspace # 构建期就验证命令可用失败即中断构建 RUN which codex codex --version ENTRYPOINT [codex]这里有两个关键点。第一ENV PATH那行不是可有可无的装饰node:20-slim这类精简镜像的默认 PATH 有时不包含/usr/local/bin而 npm 全局安装的可执行文件恰恰落在那里。第二RUN which codex codex --version是构建期自检如果这一步就失败说明问题出在安装本身而不是运行阶段的环境差异能帮你把排查范围直接砍一半。3.2 多阶段构建必须显式拷贝全局目录如果你确实要用多阶段构建那安装 Codex 的那一层和最终运行层是两个完全独立的镜像COPY --frombuilder /app /app只拷了应用代码全局 npm 目录根本没过去。正确写法是把全局安装产物一起拷FROM node:20 AS builder RUN npm install -g openai/codex # 先确认实际全局前缀不同基础镜像可能不同 RUN npm config get prefix FROM node:20-slim # 把 builder 阶段的全局 node_modules 和 bin 拷到最终镜像 COPY --frombuilder /usr/local/lib/node_modules /usr/local/lib/node_modules COPY --frombuilder /usr/local/bin /usr/local/bin ENV PATH/usr/local/bin:${PATH} WORKDIR /workspace RUN which codex codex --version ENTRYPOINT [codex]注意npm config get prefix那行它的输出决定了你要拷哪个目录。标准 node 镜像一般是/usr/local但如果你换了别的基础镜像前缀可能不一样照抄路径就会拷空。先跑一次看输出再改 COPY 的源路径。3.3 docker-compose.yml 环境变量骨架容器里注入 TaoToken 的 Key 和 base_url用 compose 管理最清晰services: codex: build: . environment: - TAOTOKEN_API_KEY${TAOTOKEN_API_KEY} - OPENAI_BASE_URLhttps://taotoken.net/api - OPENAI_API_KEY${TAOTOKEN_API_KEY} volumes: - ./workspace:/workspace - ./config.toml:/root/.codex/config.toml:ro stdin_open: true tty: trueOPENAI_BASE_URL指向 TaoToken 的 API 入口OPENAI_API_KEY复用同一个 Key这样 Codex CLI 走的就是统一通道。config.toml用只读挂载进去避免容器内被意外改写。如果你不想用环境变量也可以全部收敛到config.toml里下面给骨架。3.4 config.toml 配置骨架# ~/.codex/config.toml model gpt-4o [api] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY [history] persistence noneapi_key_env指向环境变量名而不是硬编码 Key这样 compose 注入的TAOTOKEN_API_KEY会被自动读取。persistence none在 CI 场景下比较合适避免容器销毁后残留会话文件。4. 验证请求与成功结果配置写完先构建再验证别急着跑业务命令docker build -t codex-demo . docker run --rm codex-demo codex --version如果第二条命令能打印出版本号说明exec阶段的 PATH 问题已经解决。接着验证模型通道是否通docker run --rm \ -e TAOTOKEN_API_KEY$TAOTOKEN_API_KEY \ -e OPENAI_BASE_URLhttps://taotoken.net/api \ codex-demo codex 用一句话解释什么是容器镜像层成功的话你会看到 Codex 返回一段正常文本而不是鉴权错误或连接超时。如果这一步报 401检查 Key 是否注入成功报连接错误检查 base_url 是否写成了带路径的完整地址。实测下来把which codex、codex --version、一次真实对话这三步串起来验证基本能覆盖 90% 的容器化接入问题。再补一个 CI 里常用的自检片段构建完直接跑失败就中断流水线docker build -t codex-demo . || exit 1 docker run --rm codex-demo codex --version || (echo codex 不可用构建失败 exit 1)5. 本篇常见错排查5.1 构建日志显示安装成功容器里却找不到这是多阶段构建最典型的坑。RUN npm install -g openai/codex确实在 builder 层执行成功了但最终运行层是一个全新的基础镜像没有显式 COPY 就不会有这些产物。构建日志的“成功”只代表安装那一步本身没问题不代表最终镜像完整。解决办法就是 3.2 里的显式拷贝或者干脆退回单阶段安装。5.2 本地 docker run 时灵时不灵大概率是你本地存在多个不同 tag 的镜像某次构建修好了、某次又引入了问题而运行命令时用的 tag 不一致。排查时明确指定完整 tag并用docker inspect看构建时间别混用新旧镜像。另外docker run时如果覆盖了--entrypoint也可能绕过原本的 PATH 设置这点容易被忽略。5.3 Alpine 基础镜像的差异Alpine 用的是 musl libc 而不是 glibc部分依赖原生编译的 npm 包在上面可能需要额外编译依赖甚至存在兼容性问题。如果你用 Alpine 遇到类似报错先确认 Codex CLI 是否明确支持 Alpine不确定就换回node:20-slim这类 Debian 系基础镜像能省掉一堆排查时间。5.4 入口脚本把 PATH 覆盖了有些项目会在ENTRYPOINT里包一层 shell 脚本脚本里如果重新export PATH...而没带上/usr/local/bin就会把 Dockerfile 里设好的 PATH 冲掉。检查入口脚本里所有 PATH 赋值确保是追加而不是覆盖比如写成export PATH/usr/local/bin:$PATH。5.5 排查清单速查确认 Dockerfile 是否用了多阶段构建检查安装 Codex 的阶段与最终运行阶段是否为同一层多阶段场景确认全局 npm 目录是否被 COPY检查最终镜像 PATH 是否包含/usr/local/bin用docker run -it --entrypoint sh image进容器手动验证CI 里加入构建后codex --version自检6. 接入通道与后续操作修完executable file not found只是第一步真正让 Codex CLI 在容器里跑起来还得把 Key 和 API 通道接对。如果你还在逐个配 Key建议直接去 API Keys 页面创建一个统一 Key配合上面的 compose 骨架注入容器和本地共用一套配置。接入细节可以对照接入文档里面有 base_url 和参数说明。验证模型是否真的通了用模型对话跑一次真实请求最直接比只看--version靠谱。如果你打算把 Codex CLI 长期用在编码任务或 Agent 工作流里Coding Plan 能把额度模型固定下来避免 CI 跑到一半断掉。容器化本身不难难的是把 PATH、构建层、入口脚本这三处都对齐对齐之后这套配置在本地和 CI 里都能稳定复现。