Run Claude Code on a VPS: Install, Secure, and Persist Sessions
Set up Claude Code on a remote Linux server with headless authentication, tmux session persistence, and proper security hardening. Every command verified.
Claude Code is a terminal-based AI coding agent. It reads your codebase, edits files, runs commands, and builds features from plain-language descriptions. Running it on a VPS gives you a persistent coding agent that stays alive 24/7, accessible from any device over SSH.
This guide takes you from a fresh Ubuntu VPS to a working Claude Code setup with session persistence. Every step includes a verification command so you know it worked.
What does Claude Code need from a VPS?
Claude Code sends your prompts to Anthropic's servers for inference. Your VPS only holds project files, dev tools, and the Claude Code process itself. This means you do not need a GPU or high-end hardware.
| Requirement | Minimum | Recommended |
|---|---|---|
| OS | Ubuntu 20.04 LTS | Ubuntu 24.04 LTS |
| RAM | 4 GB | 8 GB |
| Disk | 20 GB SSD | 40 GB NVMe |
| CPU | 2 vCPUs | 4 vCPUs |
| Network | Stable internet | Low-latency connection |
| Dependencies | None (native installer) | git, ripgrep (bundled) |
The native installer bundles ripgrep and requires no runtime dependencies like Node.js. A 4 GB VPS handles Claude Code and a medium-sized codebase. If you work with large monorepos or run additional services, go with 8 GB.
Account requirement: Claude Code requires a Claude Pro, Max, Teams, Enterprise, or Console account. The free Claude.ai plan does not include Claude Code access.
How do you secure a VPS before installing Claude Code?
Lock down your server before installing anything. A VPS gets hit by automated brute-force attacks within minutes of going live. These steps create a non-root user, enforce SSH key authentication, set up a firewall, and enable automatic security updates.
If you already have a hardened server, skip to How do you install Claude Code on a Linux server?. For a deeper walkthrough of each security step, see .
Create a non-root user
SSH into your server as root, then create a user with sudo privileges:
ssh root@YOUR_SERVER_IP
adduser claude
usermod -aG sudo claude
Copy your SSH key to the new user so you can log in without a password:
mkdir -p /home/claude/.ssh
cp /root/.ssh/authorized_keys /home/claude/.ssh/authorized_keys
chown -R claude:claude /home/claude/.ssh
chmod 700 /home/claude/.ssh
chmod 600 /home/claude/.ssh/authorized_keys
Verify you can log in as the new user from a second terminal before continuing. Do not close your root session yet:
ssh claude@YOUR_SERVER_IP
You should get a shell prompt as claude@yourserver. If this works, proceed.
Disable password authentication
Edit the SSH config to block password-based logins:
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
Restart SSH and verify the settings took effect:
sudo systemctl restart ssh
sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin'
Expected output (order may vary):
permitrootlogin no
passwordauthentication no
Set up the firewall
UFW (Uncomplicated Firewall) blocks all incoming traffic except what you allow. On some minimal VPS images, you may need to install it first with sudo apt install -y ufw.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable
Verify the rules:
sudo ufw status
Expected output:
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Install fail2ban
fail2ban bans IP addresses after repeated failed SSH login attempts:
sudo apt update && sudo apt install -y fail2ban
Create a local config so your settings survive package updates:
sudo tee /etc/fail2ban/jail.local > /dev/null << 'EOF'
[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 5
bantime = 3600
findtime = 600
EOF
Enable and verify:
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
Sharp eyes: the output should show active (running). The enable --now flag makes fail2ban start immediately and survive reboots.
sudo fail2ban-client status sshd
This shows the jail is active with 0 currently banned IPs (expected on a fresh setup).
Enable automatic security updates
Unattended-upgrades patches security vulnerabilities without manual intervention:
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Select "Yes" when prompted. Verify it is active:
sudo systemctl status unattended-upgrades
The output should show active (running).
How do you install Claude Code on a Linux server?
The native installer is the recommended method. It requires no dependencies, auto-updates in the background, and runs on any supported Linux distribution. The npm installation method is deprecated.
Run the installer as your non-root user (not root):
curl -fsSL https://claude.ai/install.sh | bash
This is the official Anthropic installer. If you prefer to verify before running, you can check the setup docs for SHA256 checksums and code signing details.
The installer places the binary at ~/.local/bin/claude and adds it to your PATH. Open a new shell or source your profile to pick up the change:
source ~/.bashrc
Verify the installation:
claude --version
You should see a version number like 2.x.x. Run the built-in diagnostic to check your setup:
claude doctor
This checks your system configuration, network connectivity, and authentication status. At this point, authentication will show as incomplete. That is expected.
How do you authenticate Claude Code on a headless VPS?
Claude Code normally opens a browser window for OAuth login. On a headless server with no GUI, you need a different approach. You have two options depending on your account type.
Option A: API key from Claude Console
If you have a Claude Console account (API-based billing), set the ANTHROPIC_API_KEY environment variable. This is the simplest method for headless servers.
Generate an API key at console.anthropic.com. Then store it securely on your VPS:
sudo mkdir -p /etc/claude-code
sudo tee /etc/claude-code/env > /dev/null << 'EOF'
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
EOF
sudo chmod 600 /etc/claude-code/env
sudo chown claude:claude /etc/claude-code/env
What this does: creates a dedicated file with restricted permissions (only your user can read it) instead of putting secrets in .bashrc where they show up in shell history and are readable by other processes.
Load it in your shell profile. Add this line to ~/.bashrc:
echo 'set -a; source /etc/claude-code/env; set +a' >> ~/.bashrc
source ~/.bashrc
Verify the key is loaded:
echo $ANTHROPIC_API_KEY | head -c 15
You should see sk-ant-api03-xx (the first 15 characters of your key).
Option B: OAuth token from Claude subscription
If you use a Claude Pro or Max subscription (not Console), generate an OAuth token on a machine with a browser, then transfer it to the VPS.
On your local machine (the one with a browser):
claude setup-token
This opens a browser for OAuth authentication and outputs a long-lived token starting with sk-ant-oat01-. The token is valid for one year.
Copy the token, then on your VPS, store it the same way:
sudo tee /etc/claude-code/env > /dev/null << 'EOF'
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-your-token-here
EOF
sudo chmod 600 /etc/claude-code/env
sudo chown claude:claude /etc/claude-code/env
You also need to create ~/.claude.json to skip the onboarding wizard:
cat > ~/.claude.json << 'EOF'
{
"hasCompletedOnboarding": true
}
EOF
chmod 600 ~/.claude.json
Load it in your shell profile:
echo 'set -a; source /etc/claude-code/env; set +a' >> ~/.bashrc
source ~/.bashrc
Verify authentication
Test that Claude Code can reach Anthropic's servers:
claude -p "Say hello in exactly 5 words"
The -p flag runs a one-shot prompt without entering interactive mode. If you see a five-word greeting, authentication works. If you get an authentication error, check that your environment variable is set correctly with env | grep -E 'ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN'.
How do you keep Claude Code running after SSH disconnect?
Use tmux. It creates terminal sessions that persist on the server independently of your SSH connection. Close your laptop, fly to another country, SSH back in from your phone. The session is still there, exactly where you left it.
Install tmux
sudo apt install -y tmux
Verify:
tmux -V
Configure tmux for Claude Code
Create a .tmux.conf with settings optimized for long coding sessions:
cat > ~/.tmux.conf << 'EOF'
# Increase scrollback buffer (default is 2000 lines)
set -g history-limit 50000
# Enable mouse support for scrolling and pane selection
set -g mouse on
# Start window numbering at 1
set -g base-index 1
# Reduce escape time for faster key response
set -sg escape-time 10
# Status bar with session name and time
set -g status-right '%H:%M %d-%b'
# Keep sessions alive if the shell exits unexpectedly
set -g remain-on-exit on
EOF
Start a Claude Code session
Create a named tmux session and launch Claude Code inside it:
tmux new -s claude
You are now inside a tmux session named "claude". Start Claude Code:
cd ~/your-project
claude
Claude Code is running. To detach from the session (leave it running in the background), press:
Ctrl+B, then D
You are back at your regular SSH prompt. Claude Code continues running inside tmux.
tmux quick reference
| Action | Command |
|---|---|
| Create named session | tmux new -s claude |
| Detach from session | Ctrl+B, then D |
| List sessions | tmux ls |
| Reattach to session | tmux attach -t claude |
| Kill session | tmux kill-session -t claude |
| Scroll up in tmux | Ctrl+B, then [, then arrow keys |
| Exit scroll mode | q |
How do you reconnect to a Claude Code session from another device?
SSH into your VPS from any device and reattach:
ssh claude@YOUR_SERVER_IP
tmux attach -t claude
Your Claude Code session is exactly where you left it. The conversation history, file context, and any running tasks are preserved. This works from a laptop, phone (using an SSH app like Termius), or another server.
If the session crashed or was terminated, tmux attach will fail. Start a new one:
tmux ls
If no sessions are listed, create a fresh one with tmux new -s claude.
How do you restrict what Claude Code can do on your server?
Claude Code has a permission system that controls which tools it can use. By default, it asks for confirmation before running bash commands or editing files. You can pre-approve safe commands and block dangerous ones using .claude/settings.json in your project directory.
Never use --dangerously-skip-permissions on a real server. It bypasses all safety prompts and lets Claude Code run any command without asking.
Set up a permission allowlist
Create a project-level settings file:
cd ~/your-project
mkdir -p .claude
cat > .claude/settings.json << 'EOF'
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(npm test *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(ls *)",
"Bash(cat *)",
"Read",
"Bash(grep *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force *)",
"Bash(chmod 777 *)",
"Bash(curl * | bash)",
"Read(./.env)",
"Read(./.env.*)"
]
}
}
EOF
What this does: the allow array lets Claude Code run those commands without asking each time. The deny array blocks dangerous commands entirely. Rules follow a deny-first precedence: if a command matches both allow and deny, the deny wins.
Verify the file was created with correct permissions:
ls -la .claude/settings.json
The file should be readable by your user. Since this file gets committed to git, it applies to everyone who clones the project. For personal overrides, use .claude/settings.local.json instead (automatically gitignored).
How do you monitor resources on your VPS?
Claude Code on a 4 GB VPS uses between 200-400 MB of RAM during typical operation. Large projects or multiple concurrent tasks push this higher. Setting up swap space prevents out-of-memory kills.
Add swap space
Check if swap already exists:
sudo swapon --show
If the output is empty, create a 2 GB swap file:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Make it persist across reboots by adding it to /etc/fstab:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Verify:
free -h
You should see your swap space listed. Sharp eyes: look for the Swap: row showing 2.0Gi total.
Check resource usage
Monitor memory and CPU while Claude Code is running:
htop
If htop is not installed: sudo apt install -y htop. Watch the memory bar. Claude Code should hover around 200-400 MB. If it consistently hits 80%+ of total RAM, consider upgrading your VPS or closing other services.
Check disk usage:
df -h /
Claude Code itself uses minimal disk. Your project files and git history are the main consumers.
How much does it cost to run Claude Code on a VPS?
Claude Code runs on your existing Claude subscription with no additional fee. Your total monthly cost is the VPS plus the Claude plan.
| VPS tier | Monthly cost | Claude plan | Plan cost | Total |
|---|---|---|---|---|
| 2 vCPU, 4 GB RAM | ~$5-10 | Pro | $20/mo | $25-30/mo |
| 4 vCPU, 8 GB RAM | ~$10-20 | Max | $100/mo | $110-120/mo |
| 4 vCPU, 8 GB RAM | ~$10-20 | Console (API) | Pay-per-token | $10-20 + usage |
The Pro plan includes limited Claude Code usage. The Max plan has higher rate limits suited for heavy use. Console (API) billing charges per token and suits teams that need fine-grained cost control.
For a persistent coding agent that runs around the clock, the 4 GB VPS + Pro plan at ~$25/month is the entry point.
Something went wrong?
Claude Code won't authenticate
Check your environment variable is set:
env | grep -E 'ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN'
If empty, re-source your profile: source ~/.bashrc. If the variable is set but authentication fails, verify the key is valid. OAuth tokens expire after one year. API keys can be revoked from the Console.
tmux session disappeared
tmux sessions are lost when the server reboots. After a reboot, start a new session:
tmux new -s claude
To check if the server rebooted recently:
uptime
Claude Code is slow or unresponsive
Check available memory:
free -h
If RAM is nearly full and swap is heavily used, Claude Code will slow down. Kill unused processes or upgrade your VPS. Also check your network latency to Anthropic's servers:
curl -o /dev/null -s -w '%{time_total}\n' https://api.anthropic.com
Anything under 1 second is normal. Higher values suggest network issues between your VPS and Anthropic.
Permission denied errors
If Claude Code cannot read or write files, check ownership:
ls -la ~/your-project
Files should be owned by your user (claude:claude if you followed this guide). Fix ownership:
sudo chown -R claude:claude ~/your-project
Check logs for more detail:
journalctl -u ssh -f
This shows SSH-related logs in real time. Useful for debugging connection issues.
Copyright 2026 Virtua.Cloud. All rights reserved. This content is original work by the Virtua.Cloud team. Reproduction, republication, or redistribution without written permission is prohibited.
Ready to try it yourself?
Deploy your own server in seconds. Linux, Windows, or FreeBSD.
See VPS Plans