Paste this guide into Claude Code and it will walk you through every step interactively.
VPS Setup Guide
What this gives you: AI agents that run 24/7 without your laptop being open. Morning briefings delivered before you wake up, automated reports, always-on assistants handling tasks around the clock. Your business works while you sleep.
Your laptop closes. Your AI agents keep working. This is how you set up a server that runs 24/7 so your automation stack never sleeps.
Why You Need a VPS
Running AI agents on your local machine is fine for testing. It is not fine for production. The moment you close your laptop, everything stops. Your morning briefing does not run. Your Telegram bot goes offline. Your cron jobs do not fire.
A VPS (Virtual Private Server) is a $10-20/month Linux machine that runs 24/7 in a data centre. It is always connected, always available, and you can SSH into it from anywhere in the world. Once your agents are deployed there, they operate independently of you. That is the point.
Beyond availability, a VPS gives you a clean environment. No conflicts with your local Node version. No "works on my machine" problems. You define the environment once, and it stays exactly that way.
Choosing a Provider
Two providers stand out for this use case:
Provider
Starting Price
Recommended Spec
Notes
Hetzner
~€4/mo
4GB RAM, 2 vCPU
Best value, EU-based
DigitalOcean
~$6/mo
4GB RAM, 2 vCPU
More tutorials, US-based
Minimum spec for running Claude Code agents: 4GB RAM, 2 vCPU, 40GB SSD. Claude CLI pulls models and caches context - you need headroom. If you are running more than 3 concurrent agents, go to 8GB RAM.
Operating system: Ubuntu 22.04 LTS. Do not overthink it. Every tutorial, every Stack Overflow answer, every tool assumes Ubuntu. Use it.
SSH Key Setup
Never log in with a password. SSH keys are faster, safer, and required if you want to automate deployments.
On your local machine, generate a key pair if you do not already have one:
# Generate an ED25519 key (faster and more secure than RSA)
ssh-keygen -t ed25519 -C "your@email.com"
# View your public key - this is what you add to the server
cat ~/.ssh/id_ed25519.pub
When creating your VPS, paste your public key into the provider's SSH key field. If the server already exists, copy it manually:
# Copy your public key to the server
ssh-copy-id root@YOUR_SERVER_IP
# Or manually append it
cat ~/.ssh/id_ed25519.pub | ssh root@YOUR_SERVER_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Now set up a shortcut in your SSH config so you never type the IP again:
# Edit ~/.ssh/config on your LOCAL machine
Host my-vps
HostName 187.77.176.54
User root
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
ServerAliveCountMax 3
Now you can SSH in with just: ssh my-vps
Create a Dedicated User (Never Run as Root)
Root has unlimited power. That means a single mistake - a bad script, a compromised package - can destroy the entire server. Create a dedicated user for your agent system and only use root when you absolutely must.
# SSH in as root first
ssh my-vps
# Create a dedicated user for your AI assistant
adduser jarvis
# Add to sudo group so it can install packages when needed
usermod -aG sudo jarvis
# Copy your SSH key to the new user
mkdir -p /home/jarvis/.ssh
cp /root/.ssh/authorized_keys /home/jarvis/.ssh/
chown -R jarvis:jarvis /home/jarvis/.ssh
chmod 700 /home/jarvis/.ssh
chmod 600 /home/jarvis/.ssh/authorized_keys
Update your SSH config to use this user going forward:
Host my-vps
HostName 187.77.176.54
User jarvis
IdentityFile ~/.ssh/id_ed25519
Tip: Name the user something meaningful. "jarvis" is fine if your agent system has a name. The key thing is: never do day-to-day operations as root.
Install Node.js, Bun, and pm2
SSH in as your new user and set up the runtime environment:
# Install nvm (Node Version Manager) for clean Node installs
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
# Install Node.js LTS
nvm install --lts
nvm use --lts
# Install Bun (faster runtime, great for TypeScript bots)
curl -fsSL https://bun.sh/install | bash
source ~/.bashrc
# Install pm2 globally - this keeps your processes alive
npm install -g pm2
# Verify everything installed
node --version
bun --version
pm2 --version
Then install the Claude CLI so your agents can actually run:
# Install Claude Code CLI
npm install -g @anthropic-ai/claude-code
# Authenticate (you'll need your Anthropic API key)
claude auth
Directory Structure
A clean directory structure makes everything easier to maintain. Here is the layout that works well for a multi-agent system:
/home/jarvis/
├── gateway/ # Main bot process (Telegram, Slack, etc.)
│ ├── index.ts
│ ├── router.ts
│ └── package.json
├── agents/ # One file per specialist agent
│ ├── general.md
│ ├── linkedin.md
│ ├── youtube.md
│ ├── newsletter.md
│ └── accountant.md
├── context/ # Shared context files injected into all agents
│ ├── MEMORY.md
│ ├── business-context.md
│ └── client-context.md
├── data/ # Bash scripts that pull live data from APIs
│ ├── stripe.sh
│ ├── beehiiv.sh
│ └── fireflies.sh
├── skills/ # Reusable workflows (markdown files)
│ ├── write-shorts.md
│ └── post-content.md
├── cron/ # Scheduled automation scripts
│ ├── morning-briefing.sh
│ └── weekly-content-plan.sh
└── .env # ALL secrets live here, never in code
Warning: The .env file must never be committed to Git. Add it to .gitignore immediately. Store a backup of your secrets somewhere secure like a password manager - if you lose the .env file you will need to regenerate all API keys.
pm2 - Process Management
pm2 is what keeps your bot running after you close the SSH session. Without it, your process dies the moment you disconnect.
# Start your gateway bot
cd /home/jarvis/gateway
pm2 start index.ts --name jarvis-gateway --interpreter bun
# Save the process list so it restores after reboot
pm2 save
# Generate and enable startup script (run as root, follow the output instructions)
pm2 startup
# View all running processes
pm2 list
# View live logs for a specific process
pm2 logs jarvis-gateway
# View the last 100 lines
pm2 logs jarvis-gateway --lines 100# Interactive monitoring dashboard
pm2 monit
# Restart a process (after code changes)
pm2 restart jarvis-gateway
# Stop everything
pm2 stop all
After making code changes, the workflow is: pull from git, then restart the process:
cd /home/jarvis/gateway
git pull origin main
pm2 restart jarvis-gateway
Security Hardening
A public-facing server without hardening is a target. These steps take 15 minutes and prevent the vast majority of attacks.
Firewall with ufw
# Install ufw if not present
sudo apt install ufw
# Default: deny all incoming, allow all outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (critical - do this BEFORE enabling the firewall)
sudo ufw allow ssh
# Allow HTTP and HTTPS if you're running a web server
sudo ufw allow http
sudo ufw allow https
# Enable the firewall
sudo ufw enable
# Check status
sudo ufw status verbose
fail2ban - Block Brute Force Attacks
# Install fail2ban
sudo apt install fail2ban
# Create a local config (never edit the main one)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit the local config
sudo nano /etc/fail2ban/jail.local
# Key settings to configure:# bantime = 3600 (ban for 1 hour)# findtime = 600 (10 minute window)# maxretry = 3 (3 failures = ban)# Start and enable fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
Disable Root Login and Password Auth
# Edit the SSH daemon config
sudo nano /etc/ssh/sshd_config
# Change these lines:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes# Restart SSH daemon to apply changes
sudo systemctl restart sshd
Warning: Before disabling root login and password auth, confirm you can successfully SSH in as your jarvis user with your key. If you lock yourself out, you will need to use the provider's emergency console to recover.
Monitoring: Disk, Memory, and Logs
Know what is happening on your server before problems become outages.
# Check disk usage
df -h
# Check memory usage
free -h
# Check running processes and CPU/memory
htop
# Check pm2 process health with live graph
pm2 monit
# Check system journal for errors
sudo journalctl -xe --since "1 hour ago"
# Disk usage by directory (find what's eating space)
du -sh /home/jarvis/* | sort -hr
Log files from Claude Code agents can grow quickly. Set up log rotation to prevent disk filling up:
# Install pm2 log rotate module
pm2 install pm2-logrotate
# Configure: keep 7 days of logs, max 10MB per file
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
Tip: Set up a weekly cron job that checks disk usage and messages you if it goes above 80%. A single line of bash can save you from a server outage caused by full disk.
Environment Variables on the VPS
All your secrets live in /home/jarvis/.env. Load them into shell sessions and into pm2 processes:
# Add to ~/.bashrc so they load in every session
export $(cat /home/jarvis/.env | grep -v '^#' | xargs)
# For pm2 processes, use the --env flag or ecosystem file# Create ecosystem.config.js in your gateway directory:
module.exports = {
apps: [{
name: 'jarvis-gateway',
script: 'index.ts',
interpreter: 'bun',
env_file: '/home/jarvis/.env'
}]
}
# Start using the ecosystem file
pm2 start ecosystem.config.js
Quick Deployment Checklist
Provision server (4GB RAM minimum, Ubuntu 22.04)
Generate SSH key locally, add to server
Create dedicated user, copy SSH key
Install Node.js via nvm, Bun, pm2, Claude CLI
Set up directory structure under /home/jarvis/
Clone your agent repo, create .env file
Configure ufw firewall (SSH + any web ports)
Install and configure fail2ban
Disable root login and password auth
Start processes with pm2, run pm2 save and pm2 startup
Set up log rotation with pm2-logrotate
Test: close your laptop, wait 5 minutes, check if the bot still responds
Want this set up for you?
I can provision and configure your entire VPS agent infrastructure in one session.