Could we help you? Please click the banners. We are young and desperately need the money
Running Claude Code and the OpenAI Codex CLI inside one lightweight Ubuntu Docker container gives you two powerful AI coding agents in a clean, reproducible and isolated workspace. This guide walks through every step on Ubuntu or Linux Mint: installing Docker, starting the container, creating a user that matches your host account, installing both CLIs, writing sane configuration files, logging in for the first time, and wiring up host aliases so claude and codex feel like native commands while everything they touch stays inside the container.
Updated September 2026. Both CLIs changed a lot since the first version of this guide. The main differences you will notice below:
claude install is gone.settings.json was rewritten. Several keys from the old version never existed, defaultMode belongs under permissions, and bypassPermissions only works from the user settings file.config.toml was rewritten. approval_policy = "on-failure" is deprecated, the gpt-5-codex model is retired, web_search is now a string, and network_access lives in the [sandbox_workspace_write] table.Both agents can run fully autonomously: Claude Code in bypassPermissions mode and Codex with --yolo. Anthropic's own docs state that bypassPermissions belongs in "isolated containers and VMs only". A container gives the agents a full Linux toolchain and unrestricted internet access while the only thing they can permanently touch on your machine is the one folder you mount into it. Rebuilding the container is a two-minute job, and because the container user's home directory lives on that mounted folder, your logins, settings and even the installed CLIs survive a rebuild.
sudo rightsInstall Docker from the standard repositories if it is not already available:
sudo apt update
sudo apt install docker.io -y
sudo systemctl enable --now docker
To run docker without sudo, add your user to the docker group, then log out and back in so the new group membership takes effect:
sudo usermod -aG docker "$USER"
Verify that it works before you continue:
docker run --rm hello-world
Two host directories are mounted into the container. ~/ai-agent becomes the home directory of the container user and holds your projects, both CLI installations and their configuration (.claude, .codex). /ai-agent-tmp is mounted as /tmp inside the container so temporary files do not bloat the container layer.
mkdir -p ~/ai-agent
sudo mkdir -p /ai-agent-tmp
sudo chown "$(id -u):$(id -g)" /ai-agent-tmp
sudo chmod 1777 /ai-agent-tmp
Take note of your user and group ID now, you will need them inside the container:
id
On most desktop systems the first user has uid=1000 and gid=1000. If yours differ, replace 1000 in the user creation step below.
Start a long-lived container named ai-agent. It runs sleep infinity so it stays online, and you attach interactive shells with docker exec. The image is pinned to ubuntu:26.04 (the current LTS) rather than latest, so a future major release cannot silently change your environment.
docker run -d \
--init \
--pids-limit=4096 \
--restart=unless-stopped \
--name ai-agent \
--hostname ai-agent \
--network host \
-e TZ=Europe/Zurich \
-e LANG=C.UTF-8 \
-v "$HOME/ai-agent:/ai-agent" \
-v "/ai-agent-tmp:/tmp" \
ubuntu:26.04 \
sleep infinity
docker exec, and the hostname shown in the container prompt.localhost:1455) are reachable from your host browser without any port mapping. The trade-off is that there is no network isolation between host and container. If you prefer bridge networking, drop this flag and use the terminal login fallbacks described in the login section./tmp on the host instead of inside the container layer.Open a root shell inside the running container:
docker exec -it ai-agent bash -l
If Docker complains about permissions, you have not logged out and back in since joining the docker group. Once inside, update the base image. DEBIAN_FRONTEND=noninteractive prevents packages such as tzdata from blocking the install with interactive prompts.
export DEBIAN_FRONTEND=noninteractive
apt update && apt upgrade -y
Neither CLI needs Node.js anymore, but AI agents do need build tools, version control, search utilities, language runtimes and database clients to be useful. The following package set gives them an environment comparable to a full-stack workstation. Node.js and npm are included because many MCP servers are started with npx, and Python because agents write a lot of glue scripts.
apt install -y \
build-essential \
git \
curl \
wget \
jq \
ripgrep \
fd-find \
tree \
htop \
tmux \
screen \
vim \
nano \
less \
file \
zip \
unzip \
tar \
gzip \
bzip2 \
xz-utils \
whois \
python3 \
python3-pip \
python3-venv \
python3-dev \
nodejs \
npm \
sqlite3 \
libsqlite3-dev \
postgresql-client \
libpq-dev \
redis-tools \
ca-certificates \
openssh-client \
gnupg \
lsb-release \
tzdata \
locales \
sed \
gawk \
grep \
iputils-ping \
iproute2 \
imagemagick \
ffmpeg \
libxml2-dev \
libxslt1-dev \
libyaml-dev \
libffi-dev \
libssl-dev \
libreadline-dev \
zlib1g-dev \
libcurl4-openssl-dev \
sudo
Optional extras worth considering:
npx-based MCP servers. If a project needs the newest Node LTS, install it with nvm as the container user later.The mail packages from the older version of this guide were removed: sendmail pulls in a long interactive configuration and is not something an AI agent should have anyway.
This is the most important step for a painless setup. Files written by the agents must be owned by your host user, and Claude Code refuses to start in bypassPermissions mode as root. So the agents run as a non-root user inside the container whose UID and GID match your host account, with /ai-agent as home directory. That way ~/.claude and ~/.codex resolve to /ai-agent/.claude and /ai-agent/.codex, which is ~/ai-agent/.claude and ~/ai-agent/.codex on your host.
Still inside the container as root, check whether UID 1000 is already taken. The Ubuntu image ships a default ubuntu user with that ID:
id 1000
Warning: run the following commands only inside the container. Check that your prompt shows root@ai-agent before you continue.
Remove the default user and group, then create yours. Replace yourUserName with your host username and adjust the IDs if id on the host printed something other than 1000:
userdel -r ubuntu 2>/dev/null || true
groupdel ubuntu 2>/dev/null || true
groupadd -g 1000 yourUserName
useradd -u 1000 -g 1000 -d /ai-agent -M -s /bin/bash yourUserName
cp -n /etc/skel/.bashrc /etc/skel/.profile /ai-agent/
chown -R yourUserName:yourUserName /ai-agent
What this does:
-u 1000 -g 1000: match the host IDs so ownership on the shared folder is identical on both sides.-d /ai-agent -M: use the existing mount as home without trying to create it.-s /bin/bash: a proper login shell.cp -n /etc/skel/...: since the home already exists, useradd does not copy the skeleton files. Ubuntu's default .profile adds ~/.local/bin to the PATH, which is exactly where both CLI installers place their binaries.Optionally give that user passwordless sudo inside the container. This lets the agents install missing packages themselves. It is harmless for the host because sudo only affects the container, but it also means an agent can change anything in the container, so leave it out if you want a stricter setup:
echo 'yourUserName ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/yourUserName
chmod 0440 /etc/sudoers.d/yourUserName
Leave the container:
exit
Both vendors now provide standalone installers that drop a self-updating binary into ~/.local/bin. Run them as your user, not as root. The bash -lc login shell makes sure ~/.profile (and therefore the PATH) is loaded.
docker exec -it -u yourUserName -w /ai-agent ai-agent bash -lc \
'curl -fsSL https://claude.ai/install.sh | bash'
docker exec -it -u yourUserName -w /ai-agent ai-agent bash -lc \
'curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh'
Verify both installations:
docker exec -it -u yourUserName -w /ai-agent ai-agent bash -lc 'claude --version && codex --version'
If claude or codex is not found, ~/.local/bin is missing from the PATH. Check that /ai-agent/.profile exists and contains the .local/bin block from /etc/skel/.profile.
Prefer npm? npm install -g @anthropic-ai/claude-code and npm install -g @openai/codex still work and install the same binaries, but then you have to run those commands yourself to update. The standalone installs update on their own (Claude Code) or with a rerun of the installer (Codex), which the ai-agent-update helper below takes care of.
Because the shared folder is your container home, you can edit the configuration directly from the host. Create ~/ai-agent/.claude/settings.json with your favourite editor:
mkdir -p ~/ai-agent/.claude
nano ~/ai-agent/.claude/settings.json
Paste the following. It is strict JSON, so no comments and no trailing commas:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"model": "opus",
"effortLevel": "high",
"autoUpdatesChannel": "latest",
"skipDangerousModePermissionPrompt": true,
"permissions": {
"defaultMode": "bypassPermissions",
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./config/credentials.json)",
"Read(./*.key)",
"Read(./*.pem)",
"Read(./*.p12)",
"Read(./*.pfx)",
"Read(./*.tfvars)",
"Read(./terraform.tfstate)",
"Read(./terraform.tfstate.backup)",
"Read(./.terraform/**)",
"Read(./backup/**)",
"Read(./*.bak)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(~/.gcp/**)",
"Read(~/.azure/**)",
"Read(~/.kube/config)",
"Read(~/.docker/config.json)",
"Read(~/.netrc)",
"Read(~/.npmrc)",
"Read(~/.pypirc)",
"Read(~/.bash_history)",
"Read(~/.claude.json)",
"Read(~/.claude/.credentials.json)",
"Read(~/.codex/auth.json)"
]
},
"env": {
"BASH_DEFAULT_TIMEOUT_MS": "600000",
"BASH_MAX_TIMEOUT_MS": "1800000",
"BASH_MAX_OUTPUT_LENGTH": "150000",
"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "1",
"MAX_MCP_OUTPUT_TOKENS": "50000",
"DISABLE_TELEMETRY": "1",
"DISABLE_COST_WARNINGS": "1"
}
}
What each part does, and why the file looks different from the old version:
opus alias always resolves to the current default Opus model (Opus 5 at the time of writing), so the file does not go stale when a new model ships. Use sonnet for cheaper everyday work, or a full ID such as claude-opus-5 to pin a version.latest gets every release, stable lags about a week and skips releases with known regressions.defaultMode at the top level, where Claude Code ignores it. It belongs under permissions. Note that bypassPermissions and auto only take effect from the user file ~/.claude/settings.json, never from a project's .claude/settings.json. Since the container user's home is /ai-agent, this file is the user file.Bash are dropped automatically in auto mode. The long allow list from the old version was dead weight.~/.codex/auth.json) and its own credential store. Entries like .git/**, node_modules/**, build and dist were removed because they contain no secrets and blocking them only hampers the agent. Review this list whenever you add sensitive files to a project.bashSettings, outputSettings and privacy objects never existed. The same behaviour is configured through environment variables: Bash commands may run 10 minutes by default and up to 30 minutes when the model asks for it, command output is capped at the maximum of 150,000 characters (the old value of 200,000 exceeded the limit), the working directory is restored after every command, and telemetry and cost warnings are off.Auto mode is the built-in default on Pro, Max and Team plans since August 2026. A separate classifier model reviews every risky action (curl-pipe-to-bash, force pushes, destructive git commands, sending secrets to external endpoints, and so on) and blocks it, while everything else runs without prompts. It is the recommended hands-off mode when the container is not your only line of defense, for example when you mount additional directories or forward credentials. To use it as the default, set "defaultMode": "auto" in the file above. You can also start a single session with claude --permission-mode auto and keep bypassPermissions as the file default.
Create ~/ai-agent/.codex/config.toml from the host:
mkdir -p ~/ai-agent/.codex
nano ~/ai-agent/.codex/config.toml
Codex separates two controls: sandbox_mode decides what a command can technically reach (filesystem, network), approval_policy decides when Codex stops and asks you first. This profile keeps the OS-level sandbox on, allows writes to the current workspace, enables network access inside the sandbox, and asks before escalating.
#:schema https://developers.openai.com/codex/config-schema.json
# Model: pin one or delete the line to use the current recommended default.
model = "gpt-5.6-sol"
model_reasoning_effort = "high"
model_reasoning_summary = "detailed"
# Approvals and sandbox (two independent controls)
approval_policy = "on-request"
sandbox_mode = "workspace-write"
# Web search: "cached" (default), "live", or "disabled"
web_search = "live"
# Show reasoning in the TUI
hide_agent_reasoning = false
show_raw_agent_reasoning = true
check_for_update_on_startup = true
[sandbox_workspace_write]
network_access = true
exclude_tmpdir_env_var = false
exclude_slash_tmp = false
[projects."/ai-agent"]
trust_level = "trusted"
Changes compared to the old version of this file:
gpt-5-codex and its successors up to gpt-5.4 are retired for ChatGPT sign-in. The current family is GPT-5.6 with gpt-5.6-sol (most capable, coding and research), gpt-5.6-terra (everyday work) and gpt-5.6-luna (fast and cheap), with GPT-6 Astra rolling out on top. Check the models page when you set this up, or simply delete the line and let Codex pick its recommended default.on-failure is deprecated. Use on-request for interactive sessions and never for non-interactive runs.cached serves results from an OpenAI-maintained index (safer against prompt injection), live fetches current pages.[sandbox_workspace_write] table. Keep in mind that TOML tables must come after all top-level keys, which is why the table sections are at the end..codex/config.toml files and AGENTS.md without asking every time.#:schema line enables validation and autocompletion with the "Even Better TOML" extension in VS Code.Codex still offers --dangerously-bypass-approvals-and-sandbox, alias --yolo, for the moments when the sandbox gets in the way. Instead of editing your main config back and forth, Codex now supports profile files: a file named ~/.codex/<name>.config.toml is layered on top of config.toml when you pass --profile <name>. Create ~/ai-agent/.codex/yolo.config.toml:
approval_policy = "never"
sandbox_mode = "danger-full-access"
web_search = "live"
Then start Codex with codex --profile yolo. Everything not set in the profile is inherited from config.toml. This removes the filesystem guard rails and every approval prompt, so use it only in this disposable container and switch back to the default profile when you are done. An admin-enforced requirements.toml can forbid this combination on managed machines.
Add the following block to ~/.bashrc or ~/.zshrc on the host and replace yourUserName. Unlike the old single-line aliases, these are shell functions. They pass arguments with spaces correctly, they always translate your current host directory into the matching container path, and they fall back to /ai-agent when you call them from outside the shared folder instead of failing with a confusing Docker error.
# --- AI agent container helpers -------------------------------------------
AI_AGENT_USER="yourUserName" # container user (same name and UID as on the host)
AI_AGENT_HOST_DIR="$HOME/ai-agent" # shared workspace on the host
# Map the current host directory to its path inside the container.
_ai_workdir() {
local root real
root="$(cd "$AI_AGENT_HOST_DIR" && pwd -P)"
real="$(pwd -P)"
case "$real" in
"$root"|"$root"/*) printf '/ai-agent%s\n' "${real#"$root"}" ;;
*) printf '/ai-agent\n' ;;
esac
}
# Run any command inside the container as your user, in the mapped directory.
ai() {
docker exec -it -u "$AI_AGENT_USER" -w "$(_ai_workdir)" ai-agent \
bash -lc 'exec "$@"' _ "$@"
}
# The two agents. Permission modes come from their config files.
claude() { ai claude "$@"; }
codex() { ai codex "$@"; }
# Root shell in the container for apt and debugging
alias ai-agent='docker exec -it -w /ai-agent ai-agent bash -l'
# Update OS packages and both CLIs
ai-agent-update() {
docker exec -it ai-agent bash -lc \
'export DEBIAN_FRONTEND=noninteractive; apt update && apt upgrade -y && apt autoremove -y'
ai claude update
ai bash -c 'curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh'
}
Reload your shell:
source ~/.bashrc
A few notes:
bash -lc 'exec "$@"' _ "$@" starts a login shell (so ~/.local/bin is on the PATH) and hands your arguments over untouched. The old $* inside a quoted string broke prompts containing spaces or quotes.claude alias passed --permission-mode bypassPermissions --dangerously-skip-permissions on every start. The two flags are equivalent, and with defaultMode in the settings file neither is needed. If you keep auto as the file default and want an occasional YOLO session, define an extra function: claude-yolo() { ai claude --dangerously-skip-permissions "$@"; }claude install and npm install -g @openai/codex@latest on every start. Claude Code now updates itself in the background, and Codex checks for updates on startup, so the update logic moved into ai-agent-update.ai helper is handy on its own: ai npm test, ai python3 script.py or ai bash for a user shell in the right directory.Both CLIs authenticate through your browser. With --network host the callback servers they start inside the container are reachable on your host, so the flow feels native.
Claude Code: start it from any project folder inside ~/ai-agent and follow the prompts:
cd ~/ai-agent
claude
On first start you pick a theme, then a login URL is printed. Open it in your host browser, sign in with your Claude account and the CLI picks up the token automatically. If the browser cannot reach the callback (bridge networking, remote host), run claude auth login instead, which lets you paste the authorization code into the terminal. Credentials are stored in ~/.claude/.credentials.json and session state in ~/.claude.json, both on the shared folder, so you stay logged in across container rebuilds.
Codex CLI:
codex login
Codex prints a URL and listens on localhost:1455. Open the URL on the host, sign in with your ChatGPT account, done. Without host networking use codex login --device-auth, which shows a code to enter on the OpenAI website instead. To use an API key rather than a subscription, export OPENAI_API_KEY inside the container before running codex. Tokens land in ~/.codex/auth.json. Treat that file like a password.
Finally, run a health check for Claude Code. It validates the settings file and reports anything it does not understand:
claude doctor
Both agents commit code. Give the container user a git identity once:
ai git config --global user.name "Your Name"
ai git config --global user.email "you@example.com"
For pushing to remotes you have two options. The simple one is gh auth login inside the container (if you installed the GitHub CLI), which stores a token in the shared home. The more careful one is SSH agent forwarding: add -v "$SSH_AUTH_SOCK:/ssh-agent" -e SSH_AUTH_SOCK=/ssh-agent to the docker run command so the container can use your host keys without ever seeing the private key files. Whatever you choose, remember that in bypassPermissions or YOLO mode the agent can use those credentials without asking, so consider a deploy key or a fine-grained token with limited scope.
Stay inside ~/ai-agent (or any nested project folder) and call the agents directly:
cd ~/ai-agent/my-project
claude "add input validation to the signup form and write tests"
codex "explain the caching layer in this repo"
Both CLIs use the directory you were in as their working directory, so they read and edit exactly the project you meant. Keep VS Code or another editor pointed at ~/ai-agent on the host to watch changes as they happen. Put project instructions in CLAUDE.md (Claude Code) and AGENTS.md (Codex) at the project root; both agents read them automatically.
Run the helper from time to time. It upgrades the Ubuntu packages, tells Claude Code to update immediately instead of waiting for its background check, and reruns the Codex installer, which is the official way to upgrade a standalone Codex install:
ai-agent-update
Because the container user's home is the shared folder, a rebuild does not cost you anything but the apt packages:
docker rm -f ai-agent
# run the docker run command from above again, then:
docker exec -it ai-agent bash -l
# ... repeat "Install the Developer Toolkit" and "Create a Container User"
The CLIs in /ai-agent/.local, both logins, all settings and your projects are still there. If you want a truly clean slate for the agents, delete ~/ai-agent/.claude, ~/ai-agent/.claude.json and ~/ai-agent/.codex on the host.
~/ai-agent is fair game for an agent in bypassPermissions or YOLO mode, including deleting it, so keep backups or commit often.~/.ssh, ~/.aws or the Docker socket into the container. The deny list in settings.json is a second line of defense, not a replacement for not mounting secrets in the first place.--network host gives the container the host's network identity. Services listening on your host's localhost are reachable from the container. If that matters to you, use bridge networking and the terminal login fallbacks.bubblewrap and socat and enable Claude Code's Bash sandbox with /sandbox. In an unprivileged container you also need "sandbox": { "enabled": true, "enableWeakerNestedSandbox": true } in settings.json..claude/settings.json, which you can commit for your team.~/.codex/auth.json and ~/.claude/.credentials.json live on your host disk under ~/ai-agent. Do not sync that folder to a cloud drive or commit it anywhere.With this setup, Claude Code and the OpenAI Codex CLI run side by side in a pinned Ubuntu 26.04 container as a non-root user that mirrors your host account. The configuration files match the current documentation of both vendors, the agents can work autonomously without touching anything outside one folder, logins and settings survive rebuilds, and a single ai-agent-update keeps everything current. From here you can layer on MCP servers, project-level CLAUDE.md and AGENTS.md instructions, or reuse the same container image in CI to keep your AI coding workflows consistent across machines.