A practical guide to running a work and personal (or any number of) Claude Code accounts side by side on Linux — no logging in and out.
If you use Claude Code for both work and personal projects, you've probably hit this: switching accounts means logging out, logging back in, and losing whatever session state you had. It's a small friction that adds up fast if you're context-switching several times a day.
The fix is one environment variable: CLAUDE_CONFIG_DIR. Point each account at its own directory and Claude Code keeps their credentials, conversation history, and settings completely separate. Run them in different terminal tabs, simultaneously, with zero collisions.
By default everything lives under one ~/.claude directory. Point CLAUDE_CONFIG_DIR somewhere else and you get a second, fully independent copy — separate credentials, separate history, nothing shared:
flowchart LR
subgraph TA["Terminal — work"]
A["$ claude-work"]
end
subgraph TB["Terminal — personal"]
B["$ claude-personal"]
end
A -->|"CLAUDE_CONFIG_DIR=<br/>~/.claude-work"| CW[("~/.claude-work<br/>credentials · history · settings")]
B -->|"CLAUDE_CONFIG_DIR=<br/>~/.claude-personal"| CP[("~/.claude-personal<br/>credentials · history · settings")]
CW --> RW["claude CLI process"]
CP --> RP["claude CLI process"]
RW -.-> AW["Work account"]
RP -.-> AP["Personal account"]
This guide walks through three levels of setup, from a five-minute manual configuration to a fully automated, per-project workflow:
- Part 1 sets it up by hand so you understand what's happening.
- Part 2 wraps the whole thing into a single script for adding future accounts.
- Part 3 covers advanced workflows — automatic per-directory switching, VS Code integration, and changing your global default account.
Contents
Part 1: Manual setup
Step 1: Create separate config directories
Each account gets its own home for credentials and history:
mkdir -p ~/.claude-work
mkdir -p ~/.claude-personalStep 2: Add shell aliases
Edit your shell config (~/.bashrc or ~/.zshrc):
nano ~/.bashrcAdd these lines:
alias claude-work="CLAUDE_CONFIG_DIR=$HOME/.claude-work claude"
alias claude-personal="CLAUDE_CONFIG_DIR=$HOME/.claude-personal claude"Save and reload:
source ~/.bashrcStep 3: Authenticate each account
Run each alias once and log in via the browser flow when prompted:
claude-work # log in with work account
claude-personal # log in with personal account (run in a separate terminal/tab)Once both are authenticated, they run independently — even simultaneously in separate terminal tabs — with no credential or history collisions.
Reusing an already-logged-in account
If you're already logged into claude at the default location (~/.claude, no CLAUDE_CONFIG_DIR set), you don't need to re-authenticate that account. Copy its config into one of the new directories instead:
mkdir -p ~/.claude-personal # or ~/.claude-work — whichever this account is
cp -r ~/.claude/* ~/.claude-personal/Then wire up its alias as in Step 2. Only the other account needs a fresh mkdir + browser login in Step 3.
Note:
cp -rleaves~/.claudeuntouched, so plainclaudekeeps working unchanged in the meantime. Usemv ~/.claude/* ~/.claude-personal/instead if you don't need the default location anymore — but don'trm -rf ~/.claudeafterward; leave the empty dir in place, other tools may still reference it.
Part 2: Automate it
Once you're adding a third or fourth account, doing Steps 1–3 by hand gets old. This script wraps all of it into one command.
The add-account script
It creates the config dir, adds the alias to your shell rc idempotently (safe to re-run), and either copies in an existing account or launches the login flow.
Save as ~/.local/bin/claude-add-account:
#!/bin/bash
# Usage: claude-add-account <name> [--from-existing]
set -euo pipefail
NAME="${1:?Usage: claude-add-account <name> [--from-existing]}"
FROM_EXISTING="${2:-}"
CONFIG_DIR="$HOME/.claude-$NAME"
ALIAS_LINE="alias claude-$NAME=\"CLAUDE_CONFIG_DIR=$CONFIG_DIR claude\""
# Pick the shell rc file that matches the running shell
case "$(basename "$SHELL")" in
zsh) RC_FILE="$HOME/.zshrc" ;;
*) RC_FILE="$HOME/.bashrc" ;;
esac
mkdir -p "$CONFIG_DIR"
echo "Created $CONFIG_DIR"
if [ "$FROM_EXISTING" = "--from-existing" ]; then
if [ -z "$(ls -A "$HOME/.claude" 2>/dev/null)" ]; then
echo "No existing account found at ~/.claude — nothing to copy." >&2
exit 1
fi
cp -r "$HOME/.claude/." "$CONFIG_DIR/"
echo "Copied existing ~/.claude credentials into $CONFIG_DIR"
fi
if grep -qF "alias claude-$NAME=" "$RC_FILE" 2>/dev/null; then
echo "Alias claude-$NAME already present in $RC_FILE — skipping."
else
{ echo ""; echo "$ALIAS_LINE"; } >> "$RC_FILE"
echo "Added alias to $RC_FILE"
fi
echo "Run: source $RC_FILE"
if [ "$FROM_EXISTING" != "--from-existing" ]; then
echo "Then: claude-$NAME (log in via the browser flow when prompted)"
fiMake it executable:
chmod +x ~/.local/bin/claude-add-accountUsage:
claude-add-account work # new account, needs fresh login
claude-add-account personal --from-existing # reuse creds already in ~/.claudeAfter creating an account, reload your shell (source ~/.bashrc or open a new terminal) before the alias is usable. For a --from-existing account the alias works immediately after reload — no login step needed.
Adding a fifth account, or a tenth, is the same one-liner each time — no manual dotfile editing required.
Part 3: Advanced workflows
The alias-based setup covers most use cases. These workflows are for when you want the account to switch itself, or need it working inside an editor rather than just a terminal.
Automatic per-project loading (direnv)
Instead of typing claude-work / claude-personal, you can have the right account load automatically based on which directory you're in, using direnv — a shell extension that loads/unloads environment variables per directory.
1. Install direnv
sudo apt install direnv2. Hook it into your shell — add to ~/.bashrc (or ~/.zshrc):
eval "$(direnv hook bash)" # or: eval "$(direnv hook zsh)"Reload: source ~/.bashrc
3. Add a .envrc per project
cd ~/projects/work-thing
echo 'export CLAUDE_CONFIG_DIR=$HOME/.claude-work' > .envrc
direnv allowcd ~/projects/personal-thing
echo 'export CLAUDE_CONFIG_DIR=$HOME/.claude-personal' > .envrc
direnv allowNow plain claude (no alias needed) picks the right account automatically based on your current directory. direnv allow must be run once per .envrc — a deliberate security step so directories can't silently export arbitrary env vars.
VS Code integration
Integrated terminal — works with no changes. It launches your normal interactive shell, sourcing ~/.bashrc/~/.zshrc, so the aliases and the direnv hook both work exactly as in a regular terminal.
Claude Code extension — a different story. The extension spawns the claude CLI directly rather than through an interactive shell, so:
- direnv hooks never fire for it (they only trigger on interactive shell prompts).
- It inherits environment variables from whatever process launched VS Code itself, plus any per-workspace overrides.
To get a specific account into the extension, launch VS Code from a terminal inside the already-direnv'd project directory:
cd ~/projects/work-thing
direnv allow # if not already done
code .The env var is set before VS Code starts, so the extension inherits it. This is static for that VS Code window/session — switching directories after launch, or opening a different workspace folder, won't change it. If you need seamless per-project switching within one running VS Code instance, use the integrated terminal instead.
Alternatively, set the account explicitly per workspace via VS Code: Extensions → Claude Code → settings → environmentVariables.
Switching the default account
For a single global default (used by plain claude with no alias, and picked up by the VS Code extension with no special launch order), you need a switcher that changes what plain claude resolves to.
Don't symlink ~/.claude. It looks tempting — swap the symlink target, done — but it doesn't work. When CLAUDE_CONFIG_DIR is unset, Claude Code splits its state across two locations: the ~/.claude/ directory and a separate ~/.claude.json file at your home directory root (that file is where the account identity — oauthAccount — actually lives). A symlink on ~/.claude alone never touches ~/.claude.json, so the account identity silently stays whatever it was — the switcher looks like it worked but plain claude keeps using the old account. (This is also exactly what happens if a setup gets half-built by hand — ~/.claude ends up a real directory with a stray internal symlink that nothing reads, and switching it does nothing.)
The fix: keep using CLAUDE_CONFIG_DIR-style directories (Part 1/2 — everything for one account lives in one flat directory, no split), and make plain claude pick one up by exporting CLAUDE_CONFIG_DIR itself in your shell rc, driven by a small marker file.
1. A marker file holding the current default
echo "work" > ~/.claude-default-account # start with work as default2. Export CLAUDE_CONFIG_DIR from the marker in your shell rc
Add this to ~/.bashrc (or ~/.zshrc), near the account aliases from Part 1/2:
export CLAUDE_CONFIG_DIR="$HOME/.claude-$(cat "$HOME/.claude-default-account" 2>/dev/null || echo work)"Reload: source ~/.bashrc. Plain claude now resolves to ~/.claude-work.
3. A switch script
# ~/.local/bin/claude-switch
#!/bin/bash
# Sets the global default Claude Code account (used by plain `claude`, no alias).
# Usage: claude-switch <name>
set -euo pipefail
ACCOUNT="${1:?Usage: claude-switch <name>}"
TARGET="$HOME/.claude-$ACCOUNT"
MARKER="$HOME/.claude-default-account"
if [ ! -d "$TARGET" ]; then
available=$(ls -d "$HOME"/.claude-*/ 2>/dev/null | xargs -n1 basename | sed 's/^\.claude-//' | tr '\n' ' ')
echo "No such account dir: $TARGET (available: $available)" >&2
exit 1
fi
echo "$ACCOUNT" > "$MARKER"
echo "Default Claude account is now: $ACCOUNT"
echo "Restart any open terminal/VS Code window for plain 'claude' to pick it up."chmod +x ~/.local/bin/claude-switchRun claude-switch personal to flip the global default.
Caveats:
- Only one default is active at a time — this is for changing what plain
claudemeans globally, not for running two accounts simultaneously (use the aliases or direnv for that). - Already-running
claudeprocesses (terminal or extension) keep the config dir they started with — restart them after switching. The export line only runs when a new shell starts, sobash -c '...'(non-interactive) won't pick up a change either — only a real new interactive shell/terminal does. - The alias/direnv approaches and this switcher coexist cleanly: aliases explicitly pin
CLAUDE_CONFIG_DIRregardless of the current default; plainclaudefollows whatever the marker file says, unless direnv overrides it first.
Which mechanism wins?
Plain claude can now pick up CLAUDE_CONFIG_DIR from three different places, and the aliases skip all of them. Worth knowing the order before two accounts start behaving strangely:
flowchart TD
S["You run: claude"] --> Q1{"Current directory has an<br/>allowed .envrc (direnv)?"}
Q1 -- Yes --> R1["direnv sets CLAUDE_CONFIG_DIR<br/>— that project's account wins"]
Q1 -- No --> Q2{"Shell rc exports CLAUDE_CONFIG_DIR<br/>from ~/.claude-default-account?"}
Q2 -- Yes --> R2["Global default account<br/>(set via claude-switch)"]
Q2 -- No --> R3["CLAUDE_CONFIG_DIR unset<br/>— falls back to ~/.claude"]
S2["You run: claude-work / claude-personal"] --> R4["Alias sets CLAUDE_CONFIG_DIR<br/>directly for this one command<br/>— wins no matter what's above"]
Wrapping up
Three levels, one mechanism: CLAUDE_CONFIG_DIR isolates everything Claude Code needs to keep accounts apart. Start with the manual alias setup — it takes five minutes and covers the common case of "work" and "personal." Reach for the add-account script once you're managing three or more, and only bother with direnv, VS Code wiring, or a global default if you actually hit the friction each one fixes.
Further reading
- Claude Code overview — official docs home
- Claude Code settings & environment variables — full
CLAUDE_CONFIG_DIRreference and other config options - Claude Code IDE integrations — VS Code, JetBrains, and other editor setups
- direnv — per-directory environment variable loader used in Part 3
- direnv: hooking into your shell — bash/zsh/fish hook setup
- VS Code integrated terminal — how VS Code's terminal inherits your shell environment
ln(GNU coreutils) manual — symlink behavior used by the default-account switcher in Part 3