[CLI] Multiple Codex (ChatGPT) accounts on one machine

[CLI] Multiple Codex (ChatGPT) accounts on one machine

Ran out of usage, logged out, logged into the other account, got tired of it, so I automated it.

Fast track: throw this post's URL at your AI and tell it to set this up for you.

TLDR:

  • Make several codex CLIs (two, in this post).
  • Share everything. Chat history, memory, skills, all of it stays as one. (It literally is one, thanks to symlinks.)
  • Only the login differs.
  • This way you can use two or more accounts at the same time.
  • CLI only. Running two or more desktop apps is covered in the next post.
  • The same approach works for Claude Code too, not just Codex.

How

Call the existing command codex, and the new one codex2.

1) alias


# ~/.zshrc
alias codex2='CODEX_HOME="$HOME/.codex2" codex'

The first time you run codex2 and log in, ~/.codex2/auth.json is created and you're good to go.

The catch: now codex and codex2 keep separate chat history, memory, skills, and so on.

2) symlink setup

Make ~/.codex2 a selective mirror of ~/.codex. Keep only the account file (auth.json) and the volatile runtime files separate; symlink everything else so it's shared. Save this as ~/.local/bin/codex2-sync:


#!/usr/bin/env bash
set -euo pipefail
SRC="$HOME/.codex"; DST="$HOME/.codex2"

# Kept separate (not shared): the account + volatile runtime
EXCLUDE=(auth.json ipc tmp .tmp cache log mcp-oauth-locks
         process_manager node_repl shell_snapshots computer-use
         .DS_Store .stfolder .stignore)
excluded(){ local n="$1"; for e in "${EXCLUDE[@]}"; do [[ "$n" == "$e" ]] && return 0; done; return 1; }

mkdir -p "$DST"
for path in "$SRC"/* "$SRC"/.*; do
  name="$(basename "$path")"
  [[ "$name" == "." || "$name" == ".." ]] && continue
  [[ -e "$path" ]] || continue
  excluded "$name" && continue
  target="$DST/$name"
  if [[ -L "$target" ]]; then
    [[ "$(readlink "$target")" == "$path" ]] || { rm -f "$target"; ln -s "$path" "$target"; }
  elif [[ ! -e "$target" ]]; then
    ln -s "$path" "$target"
  fi
done
# Prune dangling links whose source is gone
for link in "$DST"/* "$DST"/.*; do
  [[ -L "$link" && ! -e "$link" ]] && rm -f "$link"
done
echo "Synced $DST"

chmod +x ~/.local/bin/codex2-sync && codex2-sync

Now you can run codex (the original) and codex2 (the new one) at the same time.

Why it works

All of codex's state lives under CODEX_HOME (default ~/.codex), and the "account" is really just auth.json. So splitting auth.json alone gives you a separate account. Even with the rest of the config and history shared via symlink, the state DB (sqlite) runs in WAL mode, so two processes touching it concurrently is safe. Separate only the volatile runtime, sockets and locks, and the two run side by side.

Caveat: the desktop app is a different story

That's all it takes for the CLI. The ChatGPT desktop app, though, is a bit more involved. Next post.