Skip to content
· Updated ·Build Logs·10 min read

Claude Code on a VPS: Deploy and Build from Your Terminal

Step-by-step guide to connecting Claude Code to your VPS via SSH, automating deployments with a deploy script, and managing your site from the CLI.

TL;DR

Install Claude Code on your VPS, set up a GitHub SSH deploy key, write a one-line deploy script, and run deployments straight from your local terminal. No CI/CD platform needed. One SSH command builds and ships your site.

In This Guide click to collapse
  1. The Architecture: Local CLI to Live Site
  2. Prerequisites and VPS Setup
  3. Installing Claude Code on the VPS
  4. GitHub SSH Deploy Keys
  5. The Deploy Script
  6. Running Deployments from Claude Code
  7. Security Hardening
  8. FAQ

The Architecture: Local CLI to Live Site

The idea is simple: skip the CI/CD platform entirely. Instead of configuring GitHub Actions, Vercel hooks, or Netlify build pipelines, I use Claude Code on my local machine to SSH into the VPS and run a deploy script. One command, and the site goes from repository to production.

Stage Where What Happens
1. Develop Local machine Edit code, test locally, push to GitHub
2. Trigger Local terminal Claude Code runs ssh yourserver "~/deploy.sh"
3. Build VPS git pull, npm install, npm run build
4. Ship VPS rsync build output to web root, site is live
Four-step deploy pipeline: Claude Code locally SSHes into the VPS to run deploy.sh, which runs git pull plus npm install plus npm run build, producing a dist folder that rsyncs to public_html and the site goes live.
One command from local Claude Code, four steps on the VPS, a live site.

This works with any VPS provider. I run mine on ScalaHosting with AlmaLinux and SPanel, but the same approach applies to DigitalOcean, Hetzner, Linode, or any server you can SSH into.

One Pipeline, Zero CI/CD Platforms

No YAML configs, no build minutes quota, no webhook debugging. Your VPS does the building and serving. Claude Code just triggers it over SSH.

Prerequisites and VPS Setup

What You Need

Requirement Version / Details Purpose
VPS with SSH access Any Linux distribution Remote server to build and host
Node.js 18+ (LTS recommended) Build toolchain for Astro/Next/etc.
Git 2.x+ Pull from GitHub on the VPS
Claude Code CLI Latest Local terminal interface
GitHub repo Public or private Source of truth for your code

I use ScalaHosting with AlmaLinux 9.5 and SPanel. The control panel gives you SSH access, file management, and SSL certificates out of the box. Any provider that gives you root or user-level SSH works.

SSH Configuration with Custom Port and Alias

Most VPS providers let you change the SSH port from the default 22. This cuts down on automated brute-force noise dramatically. Set up an alias in your local SSH config so you never have to remember the port or full hostname.

# ~/.ssh/config
Host yourserver
    HostName your-server-ip
    User youruser
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Now ssh yourserver connects you directly. No flags, no port numbers.

Quick SSH Security Wins

  • Custom port: Change from 22 to something like 2222 or 6543. Stops most automated scanners.
  • Key-only auth: Set PasswordAuthentication no in /etc/ssh/sshd_config. Eliminates brute-force attacks entirely.
  • Disable root login: Set PermitRootLogin no. Always SSH as a regular user and use sudo when needed.
  • SSH alias: Saves keystrokes and prevents port/hostname typos.

Installing Claude Code on the VPS

Installation and Authentication

SSH into your VPS and install Claude Code globally via npm:

# SSH to your VPS
ssh yourserver

# Install Claude Code globally
npm install -g @anthropic-ai/claude-code

# Verify it installed
claude --version

# Authenticate (follow the browser flow)
claude auth login

The “command not found” Trap

If claude returns “command not found” after installation, your npm global bin directory is not in PATH. This is especially common on AlmaLinux, CentOS, and RHEL-based systems. See the fix below.

Environment Essentials

The most common post-install issue is PATH not including npm’s global bin directory. Fix it by adding the path to your shell profile:

# Find where npm installs global binaries
npm config get prefix
# Typically outputs: /home/youruser/.npm-global or /usr/local

# Add to your profile
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# Verify
claude --version

Environment Checklist

Check Command Expected Output
Node.js node --version v18+ or v20+
npm npm --version 8+ or 10+
Git git --version 2.x+
Claude CLI claude --version Any version number
SSH key ls ~/.ssh/github_deploy* Key files listed

GitHub SSH Deploy Keys

Why a Deploy Key

Your VPS needs to pull from GitHub, but you should not put your personal SSH key or a full-access token on a remote server. Deploy keys solve this: they grant read-only access to a single repository.

Method Scope Access Level Revocation
Deploy Key Single repository Read-only (default) Remove from repo settings
Personal Access Token All repos (or selected) Read/write (configurable) Revoke in GitHub settings
Personal SSH Key All repos you own Full read/write Remove from GitHub account

The deploy key wins for VPS use. If the server is ever compromised, the attacker gets read access to one repository, not your entire GitHub account.

Setting It Up

Generate a dedicated key on the VPS, then add the public key to your GitHub repository.

# On the VPS: generate a key pair
ssh-keygen -t ed25519 -f ~/.ssh/github_deploy -C "deploy-key-yourserver"

# Display the public key (copy this)
cat ~/.ssh/github_deploy.pub

Add the public key in GitHub under Settings > Deploy Keys > Add deploy key. Leave “Allow write access” unchecked.

Now configure SSH on the VPS to use this key for GitHub:

# ~/.ssh/config (on the VPS)
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_deploy
    IdentitiesOnly yes

Test the connection:

ssh -T git@github.com
# Should output: Hi your-github-username/your-repo-name! You've successfully authenticated...

Deploy Keys Are Single-Repo Scoped

Unlike personal SSH keys, a deploy key only works with the one repository you add it to. If you need to pull from multiple repos on the same VPS, generate a separate deploy key for each and use different Host aliases in your SSH config.

The Deploy Script

Writing deploy.sh

Create a script on the VPS that handles the full pipeline: pull, install, build, and sync. This is the single file that turns ssh yourserver "~/deploy.sh" into a complete deployment.

#!/bin/bash
set -e

PROJECT_DIR="$HOME/projects/your-repo-name"
PUBLIC_DIR="$HOME/public_html"

echo "=== Deploying site ==="

# Pull latest code
cd "$PROJECT_DIR"
git pull origin main

# Install dependencies
npm install

# Build the site
npm run build

# Sync to web root
rsync -av --delete \
  --exclude='wp-*' \
  --exclude='.htaccess' \
  --exclude='.well-known' \
  --exclude='index.php' \
  dist/ "$PUBLIC_DIR/"

echo "=== Deploy complete ==="

Make it executable:

chmod +x ~/deploy.sh

What Each Step Does

Step Command Purpose
1 set -e Exit immediately if any command fails (no silent errors)
2 git pull origin main Fetch latest code from GitHub via deploy key
3 npm install Install or update dependencies
4 npm run build Generate static output in dist/
5 rsync -av --delete Sync build output to web root, removing stale files

The Rsync Strategy

The rsync command does the heavy lifting. The --delete flag removes files from the web root that no longer exist in the build output. But some files must survive every deploy: your .htaccess, WordPress files (if running a hybrid setup), and SSL validation directories.

Exclusion Why
wp-* Preserves WordPress core files (hybrid setups)
.htaccess Keeps custom Apache rules and security headers
.well-known Preserves SSL certificate validation files
index.php Keeps WordPress entry point (hybrid setups)

The –delete Flag Is Destructive

Without the --exclude flags, rsync --delete will wipe everything in the target directory that isn’t in your build output. Double-check your exclusions before running the first deploy. One missing exclude can delete your .htaccess or SSL files.

Always Test with –dry-run First

Before your first real deploy, run rsync -avn --delete --exclude='...' dist/ ~/public_html/ with the -n (dry-run) flag. It shows exactly what would be added, deleted, or changed without touching any files. Verify the output before removing the flag.

Running Deployments from Claude Code

One-Command Deploy

With your SSH alias and deploy script in place, deploying from Claude Code is a single command:

ssh yourserver "~/deploy.sh"

Claude Code can run this directly from your local terminal. You can also ask it conversationally: “deploy the site” or “run the deploy script on the server.” It executes the SSH command and streams the output back to you.

What a Deployment Looks Like

You: deploy the site to production

Claude Code: I'll run the deploy script on your VPS.

$ ssh yourserver "~/deploy.sh"
=== Deploying site ===
Already on 'main'
From github.com:your-github-username/your-repo-name
   a1b2c3d..d4e5f6g  main -> origin/main
Updating a1b2c3d..d4e5f6g
Fast-forward
 src/pages/index.astro | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
npm install: up to date
Building site...
generating static routes
▶ src/pages/index.astro
▶ src/pages/about.astro
▶ src/pages/blog/index.astro
build complete in 4.2s
sending incremental file list
=== Deploy complete ===

Site is live. The deploy pulled 1 changed file, rebuilt, and synced to production.

Claude Code as Your DevOps Partner

Deployment is just one thing you can do remotely. Because Claude Code can run any SSH command, your VPS becomes an extension of your local terminal. Check disk space, tail logs, restart services, inspect processes, all without opening a separate SSH session.

Task Command What It Shows
Deploy ssh yourserver "~/deploy.sh" Full deploy pipeline output
Check disk space ssh yourserver "df -h" Filesystem usage on all mounts
View recent logs ssh yourserver "tail -20 ~/logs/error.log" Last 20 lines of error log
Check running processes ssh yourserver "pm2 status" PM2-managed process list and status
Restart a service ssh yourserver "pm2 restart app-name" Service restart confirmation
Memory usage ssh yourserver "free -h" RAM usage breakdown

Your VPS as an Extension of Your Terminal

The mental model shift: stop thinking of your VPS as a remote machine you “log into.” With Claude Code and SSH aliases, it becomes another tool in your local workflow. Ask Claude to check something on the server the same way you’d ask it to read a local file.

Security Hardening

Running deployments over SSH means your security baseline matters. A compromised VPS with a deploy key gives an attacker access to your repository (read-only, but still). Lock things down.

Path Permission Purpose
~/.ssh/github_deploy 600 Deploy key private key
~/.ssh/config 600 SSH client configuration
~/deploy.sh 700 Deploy script (owner-execute only)
~/.claude/ 700 Claude CLI config and credentials

Harden SSH on the VPS itself by editing /etc/ssh/sshd_config:

# /etc/ssh/sshd_config
Port 2222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3

If you run Apache, add security headers and block sensitive files in .htaccess:

# Block sensitive files
<FilesMatch "(\.env|\.git|deploy\.sh)$">
    Require all denied
</FilesMatch>

# Security headers
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set Referrer-Policy "strict-origin-when-cross-origin"

Never Commit Credentials

Your .env file, SSH keys, and API tokens must never appear in your Git repository. Add them to .gitignore and verify with git status before every commit.

The Security Minimum

These four items are non-negotiable for any VPS running deployments:

  • SSH key-only authentication (no passwords)
  • Non-default SSH port
  • Read-only deploy key (not a personal SSH key)
  • 600/700 permissions on all sensitive files

FAQ

Can I use this setup with any static site generator?

Yes. The deploy script runs npm run build and rsyncs the output directory. This works with Astro, Next.js (static export), Hugo, Eleventy, or any tool that produces a folder of static files. Just change the dist/ path in the rsync command to match your generator’s output directory (out/ for Next.js, public/ for Hugo, etc.).

What if the build fails on the VPS?

The set -e flag at the top of the deploy script stops execution on any error. If npm run build fails, the rsync step never runs, so your live site stays untouched. You’ll see the error output in your terminal. Fix the issue locally, push to GitHub, and re-run the deploy.

Is it safe to have Claude Code installed on a production VPS?

Claude Code on the VPS is optional for this workflow. The core pipeline only needs Git, Node.js, and rsync on the server. Claude Code is installed locally on your development machine and triggers the deploy over SSH. If you do install it on the VPS (for remote AI-assisted debugging), restrict access with proper file permissions on the ~/.claude/ directory.

How do I roll back a bad deployment?

The fastest rollback is reverting the commit on GitHub, then re-running the deploy script. Alternatively, keep the previous build output: modify the deploy script to copy dist/ to a timestamped backup folder before each rsync. If a deploy breaks something, rsync the backup back to the web root.

How does this differ from using the VPS as a full workstation?

This workflow treats the VPS as a deploy target: Claude Code runs on your laptop, the VPS just builds and ships. The opposite pattern is to make the VPS your primary dev machine, with Claude Code running in a persistent tmux session on the server itself. You SSH in from any device (laptop, phone) and pick up exactly where you left off. I cover the full setup, including tmux from source without sudo and dual GitHub identities, in Advanced Claude Code VPS Setup: The Full Workstation Pattern. The two patterns stack: the same VPS can be both your deploy target and your primary workstation.

Can I deploy from my phone?

Not directly with Claude Code, but you can pair this setup with a Telegram bot that runs on the VPS. Send a /deploy command from your phone, and the bot executes ~/deploy.sh locally on the server. I covered that approach in a separate article on automating VPS management with Telegram.

Why not just use GitHub Actions or Vercel?

Those are great tools. But for a personal site on a VPS you already pay for, adding a CI/CD platform introduces another moving part with its own configuration, build minutes, and potential failure points. The SSH approach has zero external dependencies: your code is on GitHub, your server builds it, done. No third-party service sitting between your repository and your live site.

Kiko Hernández Sáez

Kiko Hernández Sáez

Senior SEO & AI Growth Specialist with 10+ years building digital businesses. I write about the tools, systems, and workflows I use every day, from AI automation to terminal configs.

Share this article:

Need help building AI systems that scale?

I build custom AI agents, automation pipelines, and SEO systems for businesses.

View My Services