Repository navigation
[Bug]: Docker /v1/catalog uses an unpersisted read-only CODEX_HOME #3746
Description
Resolved in dev
#3788 (ad5285e415) persists the separate Codex home under the read-only container root. OCX and Codex authentication stores remain separate.
Verified against dev 5759d9ea2f1e7281cdc01eb9628f2e0a123fb59c. Original report by @juzijia. The attribution record was added in #3811.
Client or integration
Direct HTTP/API client
Area
Installation or packaging
Summary
The repository-maintained Docker deployment persists /home/bun/.opencodex, but the catalog resolver defaults to /home/bun/.codex/opencodex-catalog.json because CODEX_HOME is not set.
The Compose service also uses read_only: true and does not mount /home/bun/.codex. This leaves the default catalog location outside the only writable persistent volume.
The hub starts normally and /healthz and /readyz return 200, but the documented authenticated /v1/catalog acceptance check returns 404 even when a valid catalog exists in the persisted ocx-state directory.
I expected the repository-maintained Docker deployment to keep the model catalog in a writable persistent location that /v1/catalog can read.
Reproduction
- Check out tag
v2.43.0. - Generate the compatibility manifest, build the repository Docker image, bootstrap the data-plane token, and start the supplied Compose service.
- Place a valid
opencodex-catalog.jsonin the persistedocx-statedirectory at/home/bun/.opencodex. - Confirm
GET /healthzandGET /readyzreturn 200. - Run an authenticated
GET /v1/catalogrequest.
The request returns 404 because readCodexCatalogPath() resolves the catalog as /home/bun/.codex/opencodex-catalog.json, while the supplied Compose deployment persists only /home/bun/.opencodex.
Verified workaround:
services:
hub:
environment:
CODEX_HOME: /home/bun/.opencodexAfter recreating the container with this environment setting, the same authenticated /v1/catalog request returns 200. Real routed OpenAI Responses and Gemini requests also continue to work.
Version
OpenCodex v2.43.0, commit 06ec553630fa2ee51a96b5cbf694089021249194.
Operating system
Debian GNU/Linux 12 (bookworm), Linux x86_64, Docker Engine 28.5.2, Docker Compose 2.40.3.
Provider and model
Not provider-specific.
Logs or error output
GET /healthz -> 200
GET /readyz -> 200
GET /v1/catalog -> 404
{"error":{"type":"invalid_request_error","code":"catalog_not_found","message":"no materialized catalog is available"}}Redacted configuration
services:
hub:
read_only: true
volumes:
- ocx-state:/home/bun/.opencodexThe Dockerfile sets OPENCODEX_HOME=/home/bun/.opencodex but does not set CODEX_HOME.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
github-actions commentedon Sep 6, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
[-][Bug][Docker][v2.43.0] /v1/catalog uses an unpersisted read-only CODEX_HOME[/-][+][Bug]: Docker /v1/catalog uses an unpersisted read-only CODEX_HOME[/+]lidge-jun commentedon Sep 6, 2026
리뷰 · 우선순위 62 / 80
이 이슈는 저장소가 직접 제공하는 Docker/Compose 배포에서 모델 카탈로그 파일이 쓰는 홈과 볼륨으로 남겨 두는 홈이 서로 어긋나는 문제입니다. Dockerfile은
OPENCODEX_HOME=/home/bun/.opencodex만 잡고,compose.yaml도ocx-state를/home/bun/.opencodex에만 붙입니다. 그런데/v1/catalog가 읽는 카탈로그 경로는 Codex 쪽 홈인CODEX_HOME을 따라갑니다.CODEX_HOME이 비어 있으면src/codex/paths.ts의resolveCodexHome()이defaultCodexHome()으로 떨어지고, 그건 보통/home/bun/.codex입니다. 그래서 기본 카탈로그는/home/bun/.codex/opencodex-catalog.json이 됩니다.지금
dev(HEADef5a7e1c2, package2.44.0)에서도 같은 구조입니다. Dockerfile ENV에CODEX_HOME이 없고, Compose는read_only: true에/home/bun/.opencodex만 씁니다./home/bun/.codex는 쓰기 가능 볼륨도 아니고 tmpfs도 아닙니다. 운영자가opencodex-catalog.json을 영속 볼륨에 넣어도,src/server/catalog-download.ts의serializePersistedCatalog()→readCodexCatalogPath()는 그 파일을 보지 못합니다. 그래서/healthz·/readyz는 200인데 인증된GET /v1/catalog만catalog_not_found404가 납니다. 이슈에 적힌 재현과 워크어라운드(CODEX_HOME: /home/bun/.opencodex)는 현재 HEAD 코드 경로와도 맞습니다.이건 release-244의 task-input/kiro 본선은 아닙니다. 그래도 공식 Docker 경로의 설치 계약 버그라서, 문서·이미지·Compose를 쓰는 사람한테는 바로 막히는 문제입니다. 고치는 범위는 작게 잡을 수 있습니다. 런타임 카탈로그 해석기를 바꾸기보다, 이미지/Compose가 Codex 홈을 영속 볼륨과 같게 맞추는 편이 안전합니다.
Dockerfile/ ENV -OPENCODEX_HOME만 설정하고CODEX_HOME이 없음. 컨테이너 기본 유저bun에서 카탈로그가/home/bun/.codex/opencodex-catalog.json으로 감compose.yaml-read_only: true+ocx-state:/home/bun/.opencodex만 마운트./home/bun/.codex는 쓰기·영속 모두 안 됨src/codex/paths.tsresolveCodexHome/DEFAULT_CATALOG_PATH- env 없으면~/.codex계열 기본값. Docker와 계약을 맞추지 않은 채 그대로면/v1/catalog가 빈 경로를 봄src/codex/catalog/parsing.tsreadCodexCatalogPath- 설정 TOML의model_catalog_json이 없을 때 위 기본 경로로 떨어짐. Compose 기본 이미지에는 그 override도 없음src/server/catalog-download.ts/src/server/index.ts/v1/catalog- 파일이 없으면catalog_not_found404. health와 분리되어 있어서 “살아 있는데 카탈로그만 없다” 상태가 됨메인테이너의 판단이 필요한 지점
CODEX_HOME을OPENCODEX_HOME과 같은/home/bun/.opencodex로 고정할지, 아니면.codex용 볼륨을 따로 둘지compose.yamlenvironment에도 중복으로 명시해 문서화할지~/.codex에 파일을 둔 커스텀 이미지 사용자와의 호환을 changelog에 어떻게 적을지너의 추천
Dockerfile runtime ENV에
CODEX_HOME=/home/bun/.opencodex를 추가하고,compose.yaml에도 같은 environment를 적어 재현을 고정하세요. 가능하면 한 줄 smoke(영속 볼륨에 카탈로그 두고 인증GET /v1/catalog200)를 Docker 검증에 넣으세요. 카탈로그 해석기 본체를 바꾸지 마세요. 워크어라운드가 이미 원인과 처방을 증명합니다.이 댓글은 grok-bot이 작성했습니다
Ingwannu commentedon Sep 6, 2026
Confirmed against current
dev(ef5a7e1c2): the default Codex home is outside the supplied writable volume, so health/readiness can succeed while the catalog endpoint has no persisted catalog to read.I am preparing a packaging-only fix that retains
/home/bun/.codexas the Codex home and gives it a separate persistentcodex-statevolume, alongside the existingocx-statevolume. I will not change the runtime catalog resolver or merge the two homes: OpenCodex's OAuth store and Codex's native credentials both use a file namedauth.jsonwith different formats. A shared home would create an avoidable storage collision.The fix will document the catalog's actual location, keep the read-only root/non-root user/authentication boundaries, and test the image/Compose storage contract. A new volume makes the correct location writable and persistent; it does not manufacture a catalog or migrate existing custom paths automatically. Existing custom
CODEX_HOMEdeployments need to retain their explicit mapping until any deliberate migration is complete.Ingwannu commentedon Sep 6, 2026
Fix prepared in draft PR #3747, head
11248902d. It gives the existing/home/bun/.codexhome its own persistent volume while preservingocx-stateand keeping the two incompatible auth stores separate. The runtime catalog resolver and authentication policy are unchanged.Verification includes a failing-before/passing-after deployment-contract regression (51 focused tests now pass), TypeScript, Compose validation, privacy scan, documentation build, and a successful production Dockerfile build. In the actual built image, a non-root/read-only/no-network container returned health 200, unauthorized catalog 401, and authorized catalog 200. After replacing the container while retaining its two disposable volumes, the same data and results survived.
This does not automatically move a catalog previously placed in the OCX home. The PR documents the correct catalog location and explicit migration; an empty new Codex volume still correctly returns
catalog_not_founduntil a valid catalog is materialized/imported there.The PR remains draft pending full CI and independent review. Keeping the issue open until the fix lands; no existing deployment has been changed.
fix(container): persist Codex home separately [skip ci]
fix(container): persist Codex home separately [skip ci]