How to switch between profiles in Claude Code

Claude Code does not provide native support for switching between multiple accounts or profiles. Having a personal Claude account for side projects and a separate work account for professional tasks means constantly logging in and out to switch between them. This is annoying and error-prone as it is easy to forget which account is active and accidentally use the wrong one.

Problem Description

Claude Code stores all configuration, credentials, and session history in a single ~/.claude/ directory, so there is no built-in mechanism to isolate data between multiple accounts. Community has raised this problem multiple times on GitHub — feature request #20131 for native multi-account support has accumulated over 20 upvotes with no official response, and issue #22872 describes a user spending over 40 minutes trying to switch accounts.

An Anthropic engineer suggested using CLAUDE_CONFIG_DIR environment variable in issue #261 as a workaround: the idea is to point Claude Code to a different configuration directory for each account. But typing CLAUDE_CONFIG_DIR=~/work claude on every launch is tedious and easy to overlook.

Technical Solution

I took a bash function by federicotdn from the same thread as a basis and improved it into a more complete wrapper.

Function below overrides the claude command with a bash wrapper, which intercepts -p / --profile flag and sets CLAUDE_CONFIG_DIR to a profile-specific directory ~/.claude-<profile_name>. All remaining arguments are forwarded to the original claude binary via command claude keyword, which prevents recursive calls to the wrapper. Default profile is personal, so invoking claude without -p flag uses ~/.claude-personal as configuration directory. Before launching Claude Code, wrapper displays the active profile name in a colored banner to visually confirm the correct account is in use.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
claude() {
    local profile_value="personal"
    local claude_args=()

    local color_border='\033[1;38;2;148;191;243m'
    local color_label='\033[1;37m'
    local color_value='\033[1;38;2;239;120;114m'
    local color_reset='\033[0m'

    show_help() {
        cat <<'EOF'
Usage: claude [-p PROFILE] [--] [claude arguments...]

Options:
  -p, --profile PROFILE  Select profile name. Sets CLAUDE_CONFIG_DIR to
                         "$HOME/.claude-PROFILE" for the launched claude command.
  -h, --help             Show this help message and original claude help.
EOF
    }

    while [[ $# -gt 0 ]]; do
        case $1 in
            -p|--profile)
                if [[ -n "$2" && "$2" != -* ]]; then
                    profile_value="$2"
                    shift 2
                else
                    printf 'Error: %s requires a value\n' "$1" >&2
                    return 1
                fi
                ;;
            -h|--help)
                show_help
                printf '\nOriginal claude help:\n\n'
                CLAUDE_CONFIG_DIR="$HOME/.claude-$profile_value" command claude --help
                return $?
                ;;
            --)
                shift
                claude_args+=("$@")
                break
                ;;
            *)
                claude_args+=("$1")
                shift
                ;;
        esac
    done

    printf '\n'
    printf "${color_border}╔══════════════════════════════════════╗${color_reset}\n"
    printf "${color_border}${color_label}          CLAUDE PROFILE              ${color_border}${color_reset}\n"
    printf "${color_border}${color_value} %-36s ${color_border}${color_reset}\n" "$profile_value"
    printf "${color_border}╚══════════════════════════════════════╝${color_reset}\n"
    printf '\n'

    CLAUDE_CONFIG_DIR="$HOME/.claude-$profile_value" command claude "${claude_args[@]}"
}

To use it, add the function above to ~/.bashrc or ~/.zshrc file and restart the terminal. Each profile requires a one-time authentication — on first launch with a new profile name, Claude Code creates the configuration directory and prompts for login. After that, each profile maintains its own isolated credentials, settings, CLAUDE.md instructions, and session history.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Launch with default "personal" profile
claude

# Launch with work profile
claude -p work

# Launch with client-specific profile
claude --profile client-acme

# Pass additional arguments to Claude Code
claude -p work --model sonnet

P.S. CLAUDE_CONFIG_DIR does not provide complete isolation. Default ~/.claude/ directory is still created alongside the profile directory, and ~/.claude/CLAUDE.md is loaded even when CLAUDE_CONFIG_DIR points elsewhere. Local .claude/settings.local.json files are also created in project directories regardless of the CLAUDE_CONFIG_DIR value (issue #3833). State directory ~/.local/state/claude/ remains shared across all profiles (issue #15670). Also, /ide command may not work correctly with custom configuration directories (issue #4739). Despite these limitations, credentials and session history are properly separated, which covers the main use case of switching between accounts. CLAUDE_CONFIG_DIR requires an absolute path — tilde (~) expansion works in the wrapper because shell expands $HOME before passing the value, but setting CLAUDE_CONFIG_DIR=~/.claude-work directly in some contexts may silently fail (issue #519).

P.P.S. Instead of putting the wrapper function directly into ~/.bashrc, a cleaner approach is to keep it in a separate file and source it automatically. For example, the function can be stored as ~/.config/bash/.bash_claude and loaded via a block in ~/.bashrc that sources all .bash_* files from that directory:

1
2
3
4
5
6
7
if [ -d "$HOME/.config/bash" ] && [ "$(ls -A $HOME/.config/bash)" ]; then
    for file in "$HOME/.config/bash/.bash_"*; do
        if [ -f "$file" ]; then
            source "$file"
        fi
    done
fi

Such organization keeps shell configuration modular — each concern lives in its own file (.bash_claude, .bash_ssh, .bash_colors, etc.) and can be managed independently. My dotfiles repository uses this approach with an automated setup_dotfiles.sh script that deploys all configuration files and injects the sourcing block into ~/.bashrc.

P.P.P.S. Anthropic may introduce native profile switching in future versions of Claude Code, which would make this workaround unnecessary.

comments powered by Disqus