# Upgrading codex on an exe.dev VM


The `exeuntu` image ships with the [`codex`](https://github.com/openai/codex)
CLI pre-installed at `/usr/local/bin/codex`. It's the static musl release
binary from GitHub, baked into the image at build time. That means:

- There is no `apt` package backing it, so `apt upgrade` won't move it.
- There is no `codex self-update` story baked in.
- The version on your VM is frozen to whatever was the latest release the
  day the image you booted from was built.

To upgrade, replace the binary in place using the same recipe the image
uses. The one-liner, run on the VM:

```sh
sudo bash -c '
  set -euo pipefail
  case "$(uname -m)" in
    x86_64) A=x86_64-unknown-linux-musl ;;
    aarch64|arm64) A=aarch64-unknown-linux-musl ;;
    *) echo "unsupported arch: $(uname -m)" >&2; exit 1 ;;
  esac
  V=$(curl -fsSL https://api.github.com/repos/openai/codex/releases/latest | jq -r .tag_name)
  echo "installing codex $V ($A)"
  curl -fsSL "https://github.com/openai/codex/releases/download/$V/codex-$A.tar.gz" \
    | tar -xzC /usr/local/bin
  mv "/usr/local/bin/codex-$A" /usr/local/bin/codex
  chmod +x /usr/local/bin/codex
'
codex --version
```

Or from your laptop, against a named VM:

```sh
ssh my-vm.exe.xyz 'sudo bash -s' <<'EOF'
set -euo pipefail
case "$(uname -m)" in
  x86_64) A=x86_64-unknown-linux-musl ;;
  aarch64|arm64) A=aarch64-unknown-linux-musl ;;
esac
V=$(curl -fsSL https://api.github.com/repos/openai/codex/releases/latest | jq -r .tag_name)
curl -fsSL "https://github.com/openai/codex/releases/download/$V/codex-$A.tar.gz" \
  | tar -xzC /usr/local/bin
mv "/usr/local/bin/codex-$A" /usr/local/bin/codex
chmod +x /usr/local/bin/codex
EOF
```

## Pinning a specific version

To install a specific release instead of `latest`, replace the `V=...` line
with the tag you want, for example:

```sh
V=rust-v0.20.0
```

Release tags are listed at <https://github.com/openai/codex/releases>.

## Why not `npm install -g @openai/codex`?

That works too if you prefer the Node-packaged build and already have a
modern Node on the VM. The image ships the native musl binary because it's
self-contained, smaller, and faster to start. If you mix the two, remember
that `which codex` will resolve to whichever appears first on `PATH` — the
npm shim usually lands in `~/.npm-global/bin` or similar, ahead of
`/usr/local/bin`.

## Why doesn't my VM just have the latest already?

The `codex` binary is fetched during `docker build` of the `exeuntu` image,
not at VM boot. New VMs built from a freshly published image will have a
recent codex; long-lived VMs keep whatever they were created with until
you upgrade explicitly.
