Claude Code on a VPS
Run Claude Code on a VPS with SSH, Docker, and headless mode. Real commands, monitoring patterns, and security hardening for a production box.
Problem: Claude Code feels natural on your laptop, but a remote server sounds like a different sport. Maybe you want it up 24/7 for long jobs. Maybe you need it reachable from several machines without dragging environments around. Or you just want the heavy AI work off your laptop battery.
Running Claude Code on a VPS is simple once the moving parts are clear: SSH, Node.js, authentication, and Docker for isolation (optional). This guide walks the whole setup with real commands you can paste into a fresh Ubuntu box.
Picking a VPS and What You Need
Claude Code is hungrier than a typical Node app. The model itself runs on Anthropic's servers, but the local process still handles context management, file ops, and tool execution.
Minimum VPS specs:
- 2 vCPUs (4 recommended for multi-agent workloads)
- 4 GB RAM (8 GB recommended)
- 40 GB SSD storage
- Ubuntu 22.04 or 24.04 LTS
Providers that work well: Every big VPS host handles this without drama. AWS Lightsail, DigitalOcean, Hetzner, and Linode all sell $10-20/month plans that clear these specs. Hetzner wins on price-to-performance for European devs. Lightsail is the easiest if you already live inside AWS.
Things to have before you start:
- An Anthropic API key (from console.anthropic.com) or a Pro/Max subscription
- SSH access to the server
- A domain name (optional, for Remote Control access)
SSH Setup and Secure Access
Start with a blank Ubuntu server. Most providers drop you in as root with a password. Job one is locking it down.
# Connect to your new server
ssh root@your-server-ip
# Create a non-root user
adduser deploy
usermod -aG sudo deploy
# Set up SSH key authentication
mkdir -p /home/deploy/.ssh
# Copy your public key (run this from your LOCAL machine instead):
# ssh-copy-id deploy@your-server-ip
# Disable password authentication
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshdStand up a basic firewall before you install anything else:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableFrom here on, always connect as the non-root user: ssh deploy@your-server-ip.
Installing Claude Code on a Headless Server
Claude Code needs Node.js 18+. Use the NodeSource repo for the current LTS:
# Install Node.js 22 LTS
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verify installation
node --version # Should show v22.x
npm --version
# Install Claude Code globally
npm install -g @anthropic-ai/claude-code
# Verify Claude Code installed
claude --versionAuthentication: On a headless box with no browser, you authenticate with an API key.
# Set your Anthropic API key
export ANTHROPIC_API_KEY="sk-ant-..."
# Or for persistent sessions, add to your shell profile
echo 'export ANTHROPIC_API_KEY="sk-ant-..."' >> ~/.bashrc
source ~/.bashrcGot a Pro or Max subscription and want OAuth instead? Run claude once, copy the auth URL it prints, open it in a browser on any device, and paste the code back into the terminal.
Running Claude Code in Non-Interactive Mode
On a server, the useful mode is headless, not interactive chat. This is where automation gets real.
Single command execution:
# Run a single task and exit
claude -p "Review the codebase in /opt/apps/myproject and list any security issues"
# Pipe input for processing
cat error-log.txt | claude -p "Analyze these errors and suggest fixes"The -p flag (print mode) runs Claude Code non-interactively. It reads the prompt, prints the result, exits. No chat UI. No waiting on input.
Combining with SSH for remote management: Here is where a VPS-hosted Claude Code starts pulling its weight. From your local machine:
# Run Claude Code on your server from your laptop
ssh deploy@your-server "cd /opt/apps/myproject && claude -p 'Run the test suite and fix any failures'"You can also use Remote Control to jump into a running interactive session from your phone or another device, which helps when monitoring long agent runs.
Docker Deployment Workflow
Docker adds isolation and reproducibility. Something breaks, you tear down the container and spin a new one. No cleaning up a polluted server.
Create a docker-compose.yml for your Claude Code workspace:
version: "3.8"
services:
claude-workspace:
image: node:22-slim
container_name: claude-code
working_dir: /workspace
volumes:
- ./projects:/workspace
- claude-cache:/home/node/.claude
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- NODE_ENV=production
command: >
bash -c "
npm install -g @anthropic-ai/claude-code &&
tail -f /dev/null
"
restart: unless-stopped
volumes:
claude-cache:Start the workspace and use it:
# Start the container
docker compose up -d
# Execute Claude Code inside the container
docker exec -it claude-code claude -p "List all files in the workspace"
# Open an interactive session
docker exec -it claude-code claude
# View container logs
docker logs claude-codeThe tail -f /dev/null line keeps the container alive so you can exec into it as often as you like. The claude-cache volume holds Claude's auth and session data between container restarts.
For project-specific containers, use separate compose files per project. Each gets its own isolated environment with only the files it needs mounted.
Monitoring and Log Management
Long-running Claude Code sessions on a VPS need eyes on them. A process crashing quietly at 3 AM is not a thing you want.
Use tmux or screen for sessions that survive SSH disconnects:
# Start a new tmux session
tmux new -s claude-session
# Run Claude Code inside tmux
cd /opt/apps/myproject
claude
# Detach: Ctrl+B, then D
# Reattach later:
tmux attach -t claude-sessionSystemd service for always-on Claude Code workers:
# /etc/systemd/system/claude-worker.service
[Unit]
Description=Claude Code Worker
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/opt/apps/myproject
Environment=ANTHROPIC_API_KEY=sk-ant-...
ExecStart=/usr/bin/claude -p "Analyze /var/log/app.log for errors since the last check and write a summary to /opt/apps/myproject/issues/latest-report.md"
Restart=always
RestartSec=3600
[Install]
WantedBy=multi-user.targetsudo systemctl enable claude-worker
sudo systemctl start claude-worker
# Check status
sudo systemctl status claude-worker
# View logs
journalctl -u claude-worker -fCommon Errors and Real Fixes
"EACCES: permission denied, mkdir '/usr/lib/node_modules'"
You installed Node.js as root and now npm wants write access for global installs. Fix it:
sudo chown -R $(whoami) /usr/lib/node_modules
# Or better: use a node version manager like nvm"Error: Unable to open browser for authentication"
Normal on a headless box. Use the API key method above, or paste the auth URL into a browser on another device.
Claude Code process killed by OOM (Out of Memory)
Your VPS is short on RAM. Multi-agent sessions that spawn many sub-agents eat memory per agent. Fixes:
# Check current memory usage
free -h
# Add swap space if you don't have it
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabSSH connection drops during long Claude Code sessions
Set SSH keep-alive on your local machine:
# ~/.ssh/config
Host your-server
HostName your-server-ip
User deploy
ServerAliveInterval 60
ServerAliveCountMax 3Or run inside tmux (above) so the session keeps going even if SSH bounces.
Security Considerations
Running an AI coding agent on a server that holds real credentials calls for extra care.
Never commit API keys. Use environment variables or a secrets manager:
# Use a .env file that's gitignored
echo "ANTHROPIC_API_KEY=sk-ant-..." > /opt/apps/myproject/.env
# Load in your shell
set -a; source /opt/apps/myproject/.env; set +aRestrict Claude Code's file access on automated tasks. The --sandbox flag caps filesystem and network access:
claude --sandbox -p "Analyze this codebase for security issues"
Set up Fail2ban to block brute-force SSH attempts:
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2banKeep the server patched:
sudo apt update && sudo apt upgrade -y
sudo apt autoremove -yWhat's Next
A VPS-hosted Claude Code opens up workflows that local boxes can't do: 24/7 uptime, scheduled tasks that run while you sleep, and multi-agent sessions that do not drain your laptop battery. Pair it with git worktrees for isolated branch work, or use Remote Control to drive sessions from your phone.
Start small. Get Claude Code running on a basic VPS over SSH. Add Docker when you need isolation. Add systemd services when you need automation. Scale from there.
Stop configuring. Start building.
Claude Code Best Practices
Five techniques top engineers use with Claude Code every day: PRDs, modular rules, commands, context resets, and a system-evolution mindset.
Git Integration
Claude Code drives git straight from your terminal. Say what you need in plain English and the commit, branch, or PR lands with your team's conventions baked in.