- Shell 91.6%
- Dockerfile 6.6%
- Makefile 1.8%
OpenAI checks for multiple token sessions and restricts usage. Can be risky for ToS so simply with 1 session |
||
|---|---|---|
| .claude | ||
| .dir-locals.el | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| codex.sh | ||
| COPYRIGHT | ||
| Dockerfile | ||
| install.sh | ||
| Makefile | ||
| README.md | ||
Codex Jail
Run the OpenAI Codex CLI inside a rootless Podman container.
Codex needs broad filesystem access to be useful. Running it in a container gives you the convenience of a fully capable coding agent without exposing your entire home directory, system binaries, or credentials beyond what you explicitly mount.
Why a jail?
Codex CLI ships with its own sandbox and approval system, but those primitives still run on top of your real user account. A misconfigured prompt or a sandbox bypass means the agent can touch anything you can. A container inverts the model: Codex sees an empty Debian system, plus only the paths you mount in. The host filesystem, your credentials, and unrelated projects stay out of reach.
Inside the container we pass --dangerously-bypass-approvals-and-sandbox
because the container itself is the sandbox — Codex can only act on the
explicitly mounted workspace and its own state directory.
Install
git clone https://git.unitoo.it/claudiomaradonna/codex-jail
cd codex-jail
./install.sh
The installer:
- Installs Podman if it is not present (apt/dnf/pacman/brew).
- Builds the
codex-cli:latestimage and dual-tags it with the detected Codex CLI version (e.g.codex-cli:0.45.0). - Drops the
codexwrapper into~/.local/bin/.
Make sure ~/.local/bin is in your PATH:
export PATH="${HOME}/.local/bin:${PATH}"
Installer options
./install.sh --image my-codex:dev # custom image name (skips version dual-tag)
./install.sh --no-cache # full rebuild bypassing layer cache
Usage
codex . # mount current directory
codex . --model gpt-5-codex # pass native codex flags
codex --with-ssh-agent . exec "fix" # SSH agent + non-interactive exec mode
codex --image my-image . # use a custom image
codex . --mount /data:/data:ro # mount /data as read-only
codex --help # full option list
Anything the wrapper does not recognise is forwarded verbatim to codex, so
all native subcommands (exec, resume, login, …) and flags (--model,
--profile, --config, --cd, …) work as usual.
Authentication
Codex CLI supports two auth modes:
- API key — export
OPENAI_API_KEYin your shell or set it in.env.codex. The wrapper forwards it into the container. - ChatGPT sign-in — run
codex logininside the container; the resulting~/.codex/auth.jsonis persisted on the host and reused on every run.
Unlike Claude Code, Codex CLI keeps a single login per host: splitting
state across multiple sessions would force you to log in again for each
one. The wrapper therefore mounts ~/.codex-jail/ directly as
/home/codex/.codex inside the container, so auth, config, conversation
history and logs are shared across every workspace — the same way the
native codex CLI behaves on your host.
Configuration: .env.codex
Drop a .env.codex file in your workspace and the wrapper loads it. Two
categories of variables are recognised:
Script-level (consumed by codex.sh, not forwarded into the container):
| Variable | Default | CLI override |
|---|---|---|
CODEX_JAIL_IMAGE |
codex-cli |
--image |
CODEX_JAIL_WORKSPACE |
/workspace |
--container-workdir |
CODEX_JAIL_NETWORK |
(none) | --network |
CODEX_JAIL_USE_SSH |
0 |
--with-ssh-agent |
CODEX_JAIL_MOUNTS |
(none) | --mount (repeats) |
CODEX_JAIL_IGNORE |
.codexignore |
--ignore-file |
CONTAINER_MAX_MEMORY |
host RAM | --max-memory |
Container-level (forwarded as env vars inside the container):
| Variable | Notes |
|---|---|
OPENAI_API_KEY |
Codex API authentication |
| anything else | Custom vars defined in .env.codex are forwarded |
Precedence order: CLI flag → host shell env → .env.codex → built-in default.
Copy .env.example as a starting point:
cp .env.example .env.codex
.codexignore
To hide files or directories from the container, drop a .codexignore in
the workspace root. Patterns are one per line, glob-style; # starts a
comment.
# Secrets
*.pem
.env
**/*.key
# Whole directories (trailing slash)
secrets/
node_modules/
For each match the wrapper stacks a tmpfs (directories) or a read-only
/dev/null (files) on top of the workspace bind mount. The host files are
untouched; Codex just sees emptiness where they would be.
Mounts beyond your project
Only four host paths are exposed by default:
- the workspace itself (read/write)
~/.codex-jail/→/home/codex/.codex(read/write, UID-remapped)- the SSH agent socket →
/ssh-agent(only with--with-ssh-agent) - anything you add via
--mountorCODEX_JAIL_MOUNTS
Nothing else from the host is reachable.
Build manually
make build # podman build -t codex-cli:latest .
podman build -t my-codex . # equivalent direct invocation
The CACHEBUST build arg forces npm to re-resolve @openai/codex, so a
plain make build always picks up the latest published Codex CLI.
License
BSD 3-Clause — see COPYRIGHT.