Local Codex execution with Docker
The optional OpenSandbox backend uses the same supervisor protocol. See alternative sandboxes and benchmarks for setup and verification boundaries. Docker is the default runtime; OpenSandbox is the only optional runtime. Podman support was removed on 2026-10-17 at the user's request. Older Podman deployments must drain tasks and explicitly migrate their data/images before using Docker. Never point Docker at retained Podman task state or assume the two engines share volumes. No automatic migration or running-service cutover is performed by this source change.
The Local containers runner runs policy-authorized coding tasks on the deployment host. Pi still handles Telegram conversations; the coding service handles approval, identity, task state and publication. Deployment starts the supervisor; individual tasks start isolated containers when authorized.
The Codex detail page and configuration dialog explain automatic VPS startup and link here for recovery. Enabled records workspace policy; it does not mean a runner is installed, reachable or authenticated. Both sign-in methods require the supervisor and a prebuilt task image.
Configure the deployment
Docker VPS releases (automatic)
Stable GitHub releases build and transfer the application, trusted Codex supervisor
and task images. The deploy script applies deploy/codex/docker-compose.yaml,
starts the supervisor, and waits for its engine, task-image and network readiness
before stopping application writers. Existing Docker databases and the Compose
project remain in place; no database migration is needed.
The supervisor restarts automatically after a host restart.
The first release generates a private management/encryption token at
secrets/codex-runner-token (mode 600). Later releases reuse it and the
codex_runner_state volume, preserving workspace account credentials. A previously
configured CODEX_RUNNER_TOKEN is adopted; mismatched tokens fail preflight rather
than silently replacing the key. Back up the token and runner volume together.
Release-local codex.env records this token, imported immutable image IDs and the
project's task-network name. It is generated on the host and never printed or
uploaded to GitHub. The app and worker receive the same management connection.
Only the trusted supervisor mounts /var/run/docker.sock; it can control the
host Docker daemon. Use this deployment on a trusted dedicated VPS. The supervisor
has CHOWN for preparing private auth files and disables SELinux container labeling
for socket access. A stopped, credential-free input copier populates a separate volume, which tasks
mount read-only. It runs no repository code and is removed after copying;
reconciliation also removes interrupted copiers. Task containers drop all
capabilities, receive no daemon socket,
and cannot join the application/database network. Runner HTTP is private with no
published port. The supervisor joins the application and separate task networks.
After deployment, enable Codex in the workspace, choose ChatGPT device code, click Sign in with device code, and complete the displayed link/code. Existing open dialogs can use Recheck connection. No runner setup fields or commands are required in the panel. Manual deployments can merge the Docker overlay with the base Compose file, specifying the two prebuilt images and a stable private token. See VPS deployment for release and recovery details.
Local Docker application stack
Install Docker Engine and Docker Compose. Run the supervisor as root in the shipped container, or UID 1000 for a native process using a prepared Docker socket. The socket grants daemon control; only trusted supervisor infrastructure gets it. Repository jobs never receive it. Build the task image before starting the stack:
docker build --target codex-job -t localhost/deepx-codex-job:local .
The image pins Codex CLI 0.155.1 and Open Code Review CLI 1.12.12 and includes
Node 24, Bun 1.3.14, Git and Bash. Review Bot uses OCR delegation for file selection
and rules with the existing Codex model credentials. Custom images used for reviews
must retain the OCR CLI; missing or incompatible OCR fails preparation.
Use a reviewed custom image based on this target if a repository needs additional
system tools. Set CODEX_RUNNER_IMAGE to its local tag; the runner never pulls
images supplied by a model or task. Build before starting tasks.
Set these variables in the deployment's private .env or Compose environment:
CODEX_CONTAINER_ENGINE=docker
CODEX_RUNNER_TOKEN=replace-with-a-random-token-of-at-least-32-characters
CODEX_PROVIDER_NAME=AIAPI
CODEX_PROVIDER_BASE_URL=https://aiapi.abmatrix.cn/v1
CODEX_MODEL=gpt-6-astra
CODEX_REASONING_EFFORT=high
The provider name, URL, model and effort above follow this device's Codex
configuration as inspected on 2026-09-22. That configuration uses
model_provider = "proxy", wire_api = "responses", and the environment key
AIAPI_API_KEY. Save your provider key in the workspace web panel as described
below. Do not copy an existing host auth.json, the entire host Codex home,
host plugins, or host credentials into images or task volumes. No key was copied
from the device.
The workspace operator selects a saved model provider or ChatGPT device
code under Plugins → Codex → Configuration → Edit. Existing
workspaces default to the custom provider. Register API providers once under
Model settings, then select a provider and Responses-compatible model in Codex.
The task-scoped proxy uses each attempt’s selected URL/model and keeps its key in
the supervisor. Code Review can select another model from the same provider. For an existing workspace with a legacy saved key, the operator can replace or
remove it in that panel until a shared provider is selected. The following
credential details describe the compatibility path. The app
stores it encrypted with ENCRYPTION_KEY and workspace-bound authenticated data.
The panel/API return only whether a key is configured, never its value. Saved
workspace keys take precedence over the optional supervisor environment fallback
CODEX_PROVIDER_API_KEY. Removing a saved key restores that fallback; without
either key, task start fails with coding_provider_not_configured.
The worker decrypts the workspace key only for the authenticated start request.
The supervisor keeps an encrypted task copy for restart recovery, protected by a
key derived from CODEX_RUNNER_TOKEN and task-bound authenticated data. It deletes
that copy when implementation finishes or the task stops. Drain active tasks
before rotating the runner token, since older task credentials then cannot decrypt.
Keys never enter job input, task volumes, container environment or command arguments,
build arguments, API responses or routine logs. The task's generated config.toml selects the custom Responses provider
through the supervisor proxy and uses env_key = "CODEX_TASK_TOKEN". This
temporary token authorizes only model requests while that task is running; it
cannot call runner management endpoints. Setup and checks do not inherit it.
The supervisor explicitly installs system CA certificates for native Codex HTTPS requests; its build and health checks verify the bundle. See the device-login incident. Login transport errors are reported separately from rejected or unavailable device login, without exposing raw CLI output. Failed reauthentication preserves sealed auth-required state for paused tasks. Cancellation terminates both the npm launcher and native CLI in that login's isolated process group.
Operators can run Actions → Diagnose Codex device login → Run workflow to probe the deployed runner's actual CLI and OpenAI auth connectivity. It uses a fresh temporary Codex home, cancels the pending request, and removes that home. It never reads existing account caches or prints device codes, credentials or raw CLI output; logs contain only status codes and diagnostic flags.
For ChatGPT device code, save the authentication method, then select Sign in with device code. Open the displayed OpenAI link, sign in and enter the one-time code. Device login must be enabled in the account's ChatGPT security settings or workspace permissions. The panel polls until connected; Disconnect account removes the local cache and stops unfinished implementation. The saved custom provider key stays unused in this mode. ChatGPT sign-in uses Codex's built-in OpenAI provider and default model rather than the deployment's custom provider model.
Both model authentication methods support connected public and private
repositories. Repository access uses fresh GitHub App installation tokens scoped
to the selected repository: read-only for preparation and contents/pull-request write for authorized publication.
ChatGPT account authentication does not grant GitHub access. Existing maintainer,
connected-repository and execution-policy checks apply independently of visibility.
Use this option only for trusted code:
Codex can access the account token cache while implementing. The supervisor
encrypts the per-workspace cache at rest using a key derived from
CODEX_RUNNER_TOKEN. It decrypts a copy into a short-lived per-task container
auth volume during the Codex phase, saves any refreshed cache on exit, and
removes that volume. Repository setup and checks use separate containers
without it. A disconnected workspace pauses before issue creation; no issue is created while waiting for sign-in. Rotating
CODEX_RUNNER_TOKEN invalidates stored device logins; reconnect afterward.
Do not copy a host auth.json into the deployment.
Account auth is owned by the persistent supervisor, not by a long-lived task container. Sealed credentials include a generation; refreshed task copies can update only the generation they received. A late exit cannot overwrite a new login or restore a disconnected account. Existing sealed caches remain readable. Drain active tasks from older builds before upgrading; their in-flight credential copies do not carry a generation and cannot write back into the new cache. On cancellation the runner stops Codex before capturing its final auth cache.
A final Codex authentication failure sets deviceAuth.state and task state to
auth_required. Transient retry events, quota and network errors do not invalidate
account auth. Tasks retain the same private checkout and fixed verification plan,
remove their auth volume and release the runner slot. The worker sends one sign-in
notice per pause. After reconnect, it rechecks permissions, connected GitHub
repository access before requesting idempotent /resume-auth on the same
runner task. It creates a fresh auth volume; it does not recreate the issue, checkout
or publication reservation. Questions and new inputs retain their original order.
Reported token usage is cumulative across resumes. A denied request before observed
model activity counts zero usage; missing reports after observed activity are marked
as unknown usage. They are not replaced with a configured allowance and do not
block authorized continuation. Stop, disconnect, access revocation and source expiry
prevent continuation. Local checkpoint retention still applies while waiting;
expired checkouts report coding_checkpoint_expired and require a new task.
Codex refreshes account tokens during normal use; the current upstream guidance uses roughly eight days since last refresh as a refresh threshold. This is not a fixed login expiry or a guarantee of indefinite validity. No scheduled model call or permanent task container is required. See the plan and recovery boundaries.
After the shared runner token and existing application settings are configured, validate and start from the repository root on the Docker engine:
docker compose -f compose.yaml -f deploy/codex/compose.yaml config --quiet
docker compose -f compose.yaml -f deploy/codex/compose.yaml up --build -d
docker compose -f compose.yaml -f deploy/codex/compose.yaml ps codex-runner
Keep CODEX_RUNNER_TOKEN private and stable: it authenticates the app/worker
connection and protects saved account credentials. The overlay wires the private
runner URL and matching token into both app and worker. Once the supervisor is
healthy, return to Plugins → Codex → Configuration → Edit → Recheck connection.
Device-code sign-in should then become available. Sign in afterward; host startup
does not connect a ChatGPT account.
The supervisor joins the app network and a separate task network. Task containers join only the task network, have no published ports, run as UID 1000 with a read-only root filesystem, dropped capabilities and process/CPU/memory limits. Outbound access is needed for GitHub, packages and the configured provider. Both Compose overlays now use Docker; the local overlay builds the supervisor while VPS releases use imported immutable images.
Only the app and worker get CODEX_RUNNER_URL and CODEX_RUNNER_TOKEN. Only the supervisor
gets the optional provider environment fallback and Docker socket. No bot, database
or GitHub App private key is mounted into coding containers. The supervisor HTTP port is not published.
Configure repositories
- Connect the GitHub App and repository as described in GitHub setup.
- For local publication, grant the App Contents: read and write, Issues: read and write, and Pull requests: read and write, then approve the installation's permission update. No Actions permission is needed.
- Under Plugins → Codex → Repositories, add the repository's base branch and maintainers. Codex discovers environment preparation and relevant tests automatically from repository instructions; no command configuration is needed.
- Open Configuration → Edit to save the workspace provider key or complete device-code sign-in, then enable Codex.
Repository identity, maintainers and publication policy are operator configuration. Codex chooses a bounded verification plan (one to eight commands, at most 2000 characters each), including required preparation for credential-free replay. The runner freezes that plan for repairs and runs it in the check container without model/GitHub credentials. Patch integrity is always checked too. A missing/invalid plan or failed verification prevents publication. Old command overrides are discarded; migration 013 removes them from persisted repository settings and invalidates old grants.
Lifecycle and recovery
Each approved task gets a tenant-scoped name, private work volume, checkout and
Codex thread. The supervisor permits four active tasks by default, shared across
workspaces and repositories. Set CODEX_RUNNER_CONCURRENCY (1–32) in the deployment
environment to change capacity; additional approved tasks wait and retry. Capacity
reservations survive supervisor restarts, and repeated submissions never allocate
another slot. Attempts for the same continuous task remain serialized.
API-key tasks can run Codex concurrently. Device-code tasks sharing one workspace credential cache serialize their Codex implementation and repair phases to preserve refresh-token rotation. Their preparation, setup, checks and publication can overlap; separate workspace login caches can run independently. Waiting for an account stream does not discard the checkout or consume a repair attempt. The next implementation copies the latest saved credentials. This follows OpenAI's serialized auth-cache guidance.
An initial preparation container receives only a repository-scoped read token and pins the base commit. The implementation container lets Codex prepare the environment and implement without a GitHub token. A separate container replays the captured verification plan without model or GitHub credentials. The workspace survives container exit in the task volume. Custom provider Codex sessions survive there too; device-code auth and sessions in the temporary auth volume are removed after implementation.
After checks pass, a credential-free export container reads a size-limited patch.
An interrupted export can be recreated because it only reads the work volume and
has no network or credentials.
The worker rechecks actor, repository, configuration and deployment permissions
after minting a fresh repository-scoped publication token. A new container with a
fresh volume applies the patch, rejects .github/ changes, commits, pushes a unique
task branch and creates a PR. It never runs repository scripts. There is no
automatic merge. Publication has its own durable reservation and is never blindly
retried after an uncertain response.
Custom provider spend is separate from Pi chat budgets. Device-code runs use
the connected ChatGPT account's Codex entitlements. Defaults are four active tasks,
with 2 CPUs, 4 GiB RAM and 256 processes per task; provision host resources for
the selected concurrency and tune the
documented CODEX_RUNNER_* environment variables in .env.example. Patches are
limited to 5 MiB. Retained workspaces and sessions are removed after 24 hours
(configurable 1–168 hours); stopped containers and volumes are also removed.
Small task tombstones prevent replay of delayed duplicate submissions. Container
volumes consume host disk, so provision storage for the configured retention.
Stop and access revocation cancel local execution without requiring GitHub credentials. A stop during publication reports an unknown outcome because a push or PR may already exist. Workspace deletion does not undo remote writes; abandoned local work is cancelled and cleaned by the supervisor. The worker also disconnects and removes that workspace's sealed device credential after deletion; if the supervisor is temporarily unavailable, it retries on the next maintenance pass. Codex tasks have no execution-cycle, repair-count, active-time or token quotas. Docker jobs have no application deadline or container watchdog. Keep the supervisor running for cancellation, reconciliation and cleanup. A restart observes existing containers by their deterministic names, without launching duplicate work.
For an unknown outcome, inspect the matching task branch and PR before starting another request. A reserved container with an uncertain start is reconciled by its deterministic identity; it is not recreated blindly. Encrypted active-task credentials, temporary task tokens and reviewed task content are private supervisor state. Replacing the supervisor state volume loses recovery information and must not be used to retry tasks automatically.
All tasks use a private stdin/stdout Codex app-server adapter. CLI 0.155.1 is unchanged. Completed-turn
needs_input results checkpoint questions; no connection-local question ID or live
steering acknowledgement is treated as durable. The allowlist is initialize,
initialized, thread/start, thread/resume and turn/start. Relevant notifications are
turn/started, item/completed, thread/tokenUsage/updated and turn/completed. Unsupported
server requests are rejected; process cancellation interrupts active execution.
JSONL frames are capped at 4 MiB and final envelopes at 60000 UTF-8 bytes. Model
reasoning and raw protocol streams are not routine logs.
Run bun scripts/codex-protocol-proof.ts to verify the pinned real CLI against a
local fake Responses provider: current-input intake evidence constraints,
structured questions, continuation, missing-session
reconstruction, per-turn token accounting and cancellation. It creates a clean
Codex home and uses no host auth or paid model calls. The fixture passed both on
the host and bundled inside the read-only job image with no external network.
The real Docker smoke also covers question checkpoints, automatic repair/repair
exhaustion, fresh and same-PR publication with fake GitHub, remote-head fencing,
device continuation and explicit private-state erasure. Waiting for user input or
sign-in releases the task's runner slot. Retained work checkpoints can seed a new isolated cycle; expired
checkpoints reconstruct from authorized context. See continuous setup.
Local checks do not establish live model intent quality or live publication; the
staging journey remains a release gate.
For a repeatable smoke test without model calls or GitHub writes, build the
codex-job and codex-supervisor targets with the :verify tags, then run
bun scripts/codex-smoke.ts. It substitutes a local Git fixture and fake Codex
executable while exercising the real supervisor, engine socket, resource limits,
volumes, patch export and cancellation, and removes its test resources afterward.
CODEX_SMOKE_JOB_IMAGE and CODEX_SMOKE_SUPERVISOR_IMAGE can select other tags.
The smoke always uses Docker. CODEX_SMOKE_DOCKER_SOCKET selects its socket.
CI and release builds run the full Docker fixture.
When a host HTTP proxy is configured, exempt localhost with NO_PROXY/no_proxy
for tests; a loopback-only build proxy may need docker build --network=host.
Official references: Codex device login, automation auth guidance, Codex custom providers, non-interactive execution, Docker run.