This document describes how to automate Claude Code installation and authentication on headless VPS machines without requiring browser-based OAuth authentication.
Claude Code's interactive mode requires OAuth authentication via a web browser. On headless VPS machines without a GUI, this creates a chicken-and-egg problem: you can't authenticate without a browser, but the machine has no browser.
While claude -p (print mode) works with just ANTHROPIC_API_KEY, interactive mode always launches an onboarding wizard that requires browser authentication.
The solution involves three components:
- Long-lived OAuth token - Generated once on a machine with a browser
- Environment variable -
CLAUDE_CODE_OAUTH_TOKENto provide the token - Onboarding bypass - A
~/.claude.jsonfile that marks onboarding as complete
On a machine with a web browser (your local development machine), run:
claude setup-tokenThis will:
- Open a browser for OAuth authentication
- Generate a token valid for 1 year
- Display the token once (save it securely!)
The output will look like:
✓ Long-lived authentication token created successfully!
Your OAuth token (valid for 1 year):
sk-ant-oat01-xxxxx...xxxxx
Store this token securely. You won't be able to see it again.
Use this token by setting: export CLAUDE_CODE_OAUTH_TOKEN=<token>
Important: This requires a Claude Pro or Max subscription.
On the same machine where you ran setup-token, extract your account info:
cat ~/.claude.json | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(json.dumps(d.get('oauthAccount'), indent=2))"This will output something like:
{
"accountUuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"emailAddress": "your@email.com",
"organizationUuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}Save these values for the next step.
On your headless VPS, create two files:
Add to ~/.bashrc or ~/.bashrc.local:
export CLAUDE_CODE_OAUTH_TOKEN="sk-ant-oat01-your-token-here"Create ~/.claude.json with:
{
"hasCompletedOnboarding": true,
"lastOnboardingVersion": "2.1.29",
"oauthAccount": {
"accountUuid": "your-account-uuid",
"emailAddress": "your@email.com",
"organizationUuid": "your-organization-uuid"
}
}Replace the values with your actual account information from Step 2.
Note: Update lastOnboardingVersion to match your installed Claude Code version (claude --version).
source ~/.bashrc # or reconnect SSH
claudeClaude Code should start directly without the onboarding wizard.
Note: You may still be prompted to "trust" a directory the first time you run Claude in a new project folder. This is expected behavior.
For automated provisioning, you can use Ansible to deploy these configurations. Store the OAuth token in a secrets manager (like 1Password) and template the files during provisioning.
Example Ansible tasks:
- name: Configure Claude Code OAuth token in bashrc.local
copy:
content: |
# Claude Code OAuth token (managed by Ansible)
export CLAUDE_CODE_OAUTH_TOKEN="{{ claude_code_oauth_token }}"
dest: "/home/{{ target_user }}/.bashrc.local"
owner: "{{ target_user }}"
mode: '0600'
no_log: true
- name: Configure Claude Code onboarding bypass
copy:
content: |
{
"hasCompletedOnboarding": true,
"lastOnboardingVersion": "2.1.29",
"oauthAccount": {
"accountUuid": "{{ claude_code_account.account_uuid }}",
"emailAddress": "{{ claude_code_account.email }}",
"organizationUuid": "{{ claude_code_account.organization_uuid }}"
}
}
dest: "/home/{{ target_user }}/.claude.json"
owner: "{{ target_user }}"
mode: '0600'Make sure your ~/.bashrc sources ~/.bashrc.local:
[[ -f ~/.bashrc.local ]] && . ~/.bashrc.localCLAUDE_CODE_OAUTH_TOKEN- Uses your Claude Pro/Max subscriptionANTHROPIC_API_KEY- Uses API credits (pay-per-use)
Do not set both - Claude Code will warn about auth conflicts. Choose one:
- For subscription-based usage: use
CLAUDE_CODE_OAUTH_TOKEN - For API credit-based usage: use
ANTHROPIC_API_KEY
The OAuth token is valid for 1 year. You'll need to regenerate it before expiration by running claude setup-token again on a machine with a browser.
- Store the OAuth token securely (secrets manager, encrypted vault)
- Set restrictive permissions on
~/.bashrc.local(mode 0600) - The token grants full access to your Claude subscription
This was tested with Claude Code version 2.1.29. The lastOnboardingVersion field may need to be updated for future versions if Claude Code changes its onboarding flow.
- Verify
CLAUDE_CODE_OAUTH_TOKENis set:echo $CLAUDE_CODE_OAUTH_TOKEN | head -c 30 - Verify
~/.claude.jsonexists and has correct content - Make sure you're not also setting
ANTHROPIC_API_KEY
This happens when using claude -p (print mode) with an OAuth token. Print mode may require API credits even with a subscription token. For interactive use, this shouldn't be an issue.
If you see a warning about both token and API key being set, unset the one you don't want:
unset ANTHROPIC_API_KEY # If using OAuth token
# or
unset CLAUDE_CODE_OAUTH_TOKEN # If using API key