DEV Community

Cover image for [CLI] Multiple Codex (ChatGPT) accounts on one machine
Sanghun Kang
Sanghun Kang

Posted on Edited on Originally published at sanghunkang.com

[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. For simultaneous use, see the database-locking caveat below.
  • 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'
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode
chmod +x ~/.local/bin/codex2-sync && codex2-sync
Enter fullscreen mode Exit fullscreen mode

Now you can run codex (the original) and codex2 (the new one) as separate commands. The limitations of simultaneous use are explained below.

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.

Both accounts use the same SQLite databases to share conversation history. WAL supports concurrent access, but Codex's lock handling can cause startup failures or temporary stalls when multiple instances run at the same time.

I switch to the other account when one account runs out of usage. I do not run both instances at the same time, and I have not encountered these issues in my day-to-day use.

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.

This article was originally published on Sanghun Kang’s blog.

Top comments (0)