GuidesHosting choices

Codex CLI on a Linux server: install, login, bubblewrap

Install Codex CLI on a VPS, sign in with codex login --device-auth, get its bubblewrap sandbox working on Ubuntu 24.04, and fix command not found over SSH.

October 1, 2026The Everpod team
The short answer

Install Codex with OpenAI’s script or with npm, then sign in with codex login --device-auth: it prints a link and a code you approve from any browser, once device code sign-in is switched on in your ChatGPT security settings. On Ubuntu 24.04, install bubblewrap and load an AppArmor profile that lets it create user namespaces, or Codex’s sandbox fails on every command. If codex works when you log in but is “command not found” from ssh server 'codex …', cron or a service, those never put ~/.local/bin on PATH: call Codex by its full path. Start interactive sessions inside tmux so they outlive your connection.

Install it

OpenAI’s documented install for Linux is a script, and it handles x86_64 and arm64 servers alike:

curl -fsSL https://chatgpt.com/codex/install.sh | sh

The script unpacks the release under ~/.codex and links the codex command into ~/.local/bin (set CODEX_INSTALL_DIR to choose another folder). If that folder is not on your PATH yet, it appends an export line to ~/.bashrc, or ~/.zshrc for zsh, which new interactive shells read. In the shell you installed from, add it yourself:

export PATH="$HOME/.local/bin:$PATH"
codex --version

Running the install command again updates Codex. The current release on October 1, 2026 is 0.159.3.

The other route is npm, with Node 16 or newer: npm install -g @openai/codex. npm links global commands into the bin folder of its prefix, and the prefix defaults to wherever Node is installed. With Node from a system package, such as Ubuntu’s or NodeSource’s, the prefix is a system folder, so the install needs sudo and codex lands in a system folder such as /usr/bin. With nvm, or an npm prefix in your home folder, it lands in your home, and the PATH section below applies to you too. The repository’s README also lists single-file release archives for both architectures, whose binary you rename to codex and put on your PATH yourself.

Sign in without a browser

Plain codex login waits for a browser on the same machine to call back to localhost:1455, and a server has no browser. Use device code sign-in, which OpenAI recommends for headless machines and still labels beta:

codex login --device-auth

Codex prints a link, auth.openai.com/codex/device, and a one-time code that expires in 15 minutes. Open the link on your laptop or phone, sign in to the ChatGPT account Codex should use, and enter the code. The terminal completes on its own. Then check:

$ codex login status
Logged in using ChatGPT

The flow needs one setting on ChatGPT’s side. For a personal account it is a switch under Settings → Security and login, labelled “Enable device code sign-in for Codex, Excel, PowerPoint, and Word” as of October 2026. For a workspace account, an admin enables device code sign-in in the workspace’s permissions. While it is off, sign-in stops with a message telling you to turn it on; the guide to that message covers it, and the errors that look like it. On a fresh Ubuntu 24.04 cloud server on September 29, 2026, with that switch on, those two commands were the whole sign-in.

If device codes are not an option, OpenAI documents two fallbacks. One forwards the callback port from your laptop and runs the ordinary sign-in through it:

# on your laptop
ssh -L 1455:localhost:1455 you@your-server
# then, in that session
codex login

Open the address it prints in your laptop’s browser. The other is to sign in on a machine that has a browser and copy its ~/.codex/auth.json to the same place on the server. The file holds live tokens, so treat it like a password. For scripts billed to an API key instead of a ChatGPT plan, codex login --with-api-key reads the key from standard input.

The sandbox on Ubuntu: bubblewrap and user namespaces

Codex runs the commands it chooses inside a sandbox, and on Linux that sandbox is bubblewrap plus a seccomp filter; release 0.115 replaced the older Landlock-based one. Bubblewrap makes the whole filesystem read-only except the folders Codex may write, normally your project, and by default it cuts the command off from the network. To build that view it creates unprivileged user namespaces, and on Ubuntu 24.04 that is the step that fails.

Codex uses the first bwrap on your PATH. When there is none, it falls back to a copy it ships and warns at startup:

Codex could not find bubblewrap on PATH. Install bubblewrap
with your OS package manager.

Install the package either way. On Ubuntu 24.04, bubblewrap also needs an AppArmor profile: since 23.10, Ubuntu lets a program create unprivileged user namespaces only when an AppArmor profile allows it, and a fresh 24.04 server has that restriction on:

$ sysctl kernel.apparmor_restrict_unprivileged_userns
kernel.apparmor_restrict_unprivileged_userns = 1

With the restriction on and no profile for bwrap, the startup warning becomes this one:

Codex's Linux sandbox uses bubblewrap and needs access to create
user namespaces.

Every sandboxed command then fails before it starts, with bubblewrap errors like these, reported from an Ubuntu 24.04 machine with bubblewrap 0.9.0:

bwrap: setting up uid map: Permission denied
bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted

With approvals on, Codex then asks to rerun each failed command outside the sandbox: one July 2026 report from an Ubuntu 24.04 server counted around 15 permission prompts in a single turn.

Let bubblewrap create namespaces

OpenAI’s fix for Ubuntu 24.04 installs bubblewrap and loads the bwrap-userns-restrict profile, which comes with the apparmor-profiles package but is not enabled. It lets /usr/bin/bwrap create namespaces and strips capabilities from everything that runs inside them:

sudo apt update
sudo apt install bubblewrap apparmor-profiles apparmor-utils
sudo install -m 0644 \
  /usr/share/apparmor/extra-profiles/bwrap-userns-restrict \
  /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict

On Ubuntu 25.04, OpenAI’s docs say the apparmor package already installs that profile, so the bubblewrap package alone is enough.

Anthropic’s Claude Code docs give a shorter profile for the same binary, which grants bwrap user namespaces and leaves it otherwise unconfined:

sudo tee /etc/apparmor.d/bwrap > /dev/null << 'EOF'
abi <abi/4.0>,
include <tunables/global>

profile bwrap /usr/bin/bwrap flags=(unconfined) {
  userns,

  include if exists <local/bwrap>
}
EOF
sudo systemctl reload apparmor

On the server above, with Ubuntu’s bubblewrap package and this profile loaded, Codex’s sandbox ran a command and refused a write outside the folders it may write. Both files define the profile for /usr/bin/bwrap, so load one, not both; if Claude Code shares the server, its sandbox uses the same binary. Neither covers the copy Codex bundles, which is why the package matters.

The last resort in OpenAI’s docs switches the restriction off for every program on the machine, not just bubblewrap, until the next reboot:

sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

If Codex runs inside a Docker container on the server, the container’s own confinement can refuse the same namespaces: these are the two gates that stop Chromium’s sandbox in a container. OpenAI’s advice there is to make the container the boundary and run Codex inside it with --sandbox danger-full-access.

Check that it works

codex sandbox runs any command under the policy Codex applies to its own. From inside a project:

cd ~/your-project
codex sandbox -- ls
codex sandbox -- touch ../outside.txt

The first should list the folder. The second should fail, because nothing outside the project is writable. A bwrap: error on either means the namespace fix has not taken. Older posts, and the security page of OpenAI’s own docs as of October 1, show the form codex sandbox linux …. Current releases choose the platform themselves and take the command after --, so that form tries to run a program called linux and fails, as it did on the server above.

The sandbox’s network is off by default, so a command that fetches something fails inside it and Codex asks to run it outside instead, which is what it did there for a web request. To give sandboxed commands network access, add this to ~/.codex/config.toml:

[sandbox_workspace_write]
network_access = true

“codex: command not found” over SSH or from cron

The pattern: codex runs in a terminal you log into, but ssh you@server 'codex …', a cron job or a systemd service reports codex: command not found. On the same server, with Codex in ~/.local/bin, commands sent as ssh server 'command' ran without that folder on their PATH. On Ubuntu, three things combine:

The dependable fix is the full path, which command -v codex prints in a login session:

ssh you@server '~/.local/bin/codex login status'

# or run it through a login shell, which reads ~/.profile
ssh you@server 'bash -lc "codex login status"'

To make plain ssh server 'codex …' work, put the PATH line above that check, as the first line of ~/.bashrc:

export PATH="$HOME/.local/bin:$PATH"

For cron, write the full path into the job, and give codex exec, the non-interactive mode, what it expects: a Git repository to run in (or --skip-git-repo-check), and --sandbox workspace-write if it should edit files, since it runs read-only by default. It reuses your saved sign-in, and cron runs the job with your home folder, so it finds it.

# crontab -e
0 7 * * * cd $HOME/project && $HOME/.local/bin/codex exec --sandbox workspace-write "update CHANGELOG.md from yesterday's commits" >> $HOME/codex-cron.log 2>&1

In a systemd unit, give ExecStart= the full path too.

The ChatGPT desktop app’s SSH hosts are the case where the login shell is what counts. OpenAI’s remote connections docs say the app starts Codex on the host “using the remote user’s login shell” and needs codex on that shell’s PATH. This shows whether it will find it:

ssh you@server 'bash -lc "command -v codex"'

Keep a session running after you disconnect

An interactive codex ends when the SSH connection that started it closes. Start it inside tmux and it stays on the server while you are gone:

tmux new -s codex
codex
# detach: Ctrl-b d    reattach later: tmux attach -t codex

Any device that can SSH in can reattach to the same session, mid-task, and the tmux guide has the few commands worth knowing. On a connection that drops often, mosh keeps the terminal itself alive across network changes and pairs with tmux. If a session did end, codex resume --last reopens the most recent conversation, and codex resume shows a picker of earlier ones.

Steering the server from your phone, without a terminal, is a separate setup: Codex remote control pairs the ChatGPT app with a desktop app host, which can open projects on an SSH host like this one. For the wider pattern, an always-on box for a coding agent and the security posture it needs, see running Claude Code on a VPS.

Your own open-source AI agent, set up for you.

Everpod runs OpenClaw on a private, always-on computer of its own: set up, secured and backed up, with model usage included. You name your agent, and say hello about fifteen minutes later.

Create your agent

First month half price, then $29/mo · model usage included · cancel anytime

Wondering what you’d do with one? See what a cloud agent can do