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

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

Deploy your site from Claude Code with one SSH command: a read-only GitHub deploy key, a safe deploy script, and no CI/CD platform.

TL;DR

Set up a read-only GitHub deploy key on your VPS, write a one-file deploy script, and trigger it from Claude Code on your laptop with one SSH command. No CI/CD platform needed.

In This Guide click to collapse
  1. The Architecture: Local CLI to Live Site
  2. Prerequisites and VPS Setup
  3. Installing Claude Code (Locally, and Optionally 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 ci, 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 a RHEL-family distribution (AlmaLinux or Rocky Linux 9) 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 22 LTS or newer (check your framework’s minimum) Build toolchain for Astro/Next/etc.
Git 2.x+ Pull from GitHub on the VPS
Claude Code CLI Latest (native installer) Local terminal interface
GitHub repo Public or private Source of truth for your code

I use ScalaHosting with a RHEL-family distribution (AlmaLinux or Rocky Linux 9) 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 (Locally, and Optionally on the VPS)

Installation and Authentication

Install Claude Code on your own machine first; that is where it runs in this workflow. Anthropic’s recommended method is the native installer, which needs no Node.js:

# macOS, Linux, WSL
curl -fsSL https://claude.ai/install.sh | bash

# Verify it installed
claude --version

# Start Claude Code and sign in when prompted (browser flow)
claude

On Windows PowerShell the equivalent is irm https://claude.ai/install.ps1 | iex. Check the current Claude Code setup docs if either command has changed. The older npm install -g @anthropic-ai/claude-code route still works as a fallback, but it is no longer the recommended path.

Installing it on the VPS as well is optional. Only do it if you want AI-assisted debugging on the server itself; the same installer works there, and it installs into ~/.local/bin without sudo.

The “command not found” Trap

If claude returns “command not found” after installation, the install directory (~/.local/bin for the native installer) is not in PATH. This is especially common on AlmaLinux, Rocky Linux, CentOS, and other RHEL-based systems. See the fix below.

Environment Essentials

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

# Native installer location
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# Verify
claude --version

# Only if you used the npm fallback: find its global bin directory
npm config get prefix

Environment Checklist

Check Command Expected Output
Node.js node --version v22 LTS or newer
npm npm --version 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 (reproducible, uses package-lock.json)
npm ci

# 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 ci Install exact dependency versions from the lockfile
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

Guardrails when an agent can run your deploy

Letting an AI agent run ssh commands is useful only if the blast radius is small. Here that means a read-only deploy key, a deploy script that stops on the first error (set -e), a dry-run step before rsync --delete, and a human who approves the command. The same pattern, an agent that proposes and a person who approves, is how I design larger marketing automations. See AI agents with human-in-the-loop guardrails and the approval-gate pattern in detail.

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 chmod 700 ~/.claude, since that directory holds your login credentials.

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 may cover that approach in a future post. For a team version, the same deploy script can sit behind a chat command with an approval step, as described in human-in-the-loop approval gates for AI agents.

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.

Can an AI agent deploy my site safely?

Yes, if you limit what it can break. Give the server a read-only deploy key, keep the deploy script strict (set -e, a dry-run before rsync --delete), and make the agent show you the exact command before it runs. The agent proposes, you approve. For bigger workflows, add an explicit approval step; see human-in-the-loop approval gates for AI agents.

Kiko Hernández Sáez

Kiko Hernández Sáez

Senior AI Marketing Automation Specialist with 8+ years leading high-ROI campaigns. I write about AI agents, automation, SEO and YouTube growth.

Share this article:

Need AI agents or marketing automation that scale?

Custom AI agents, automation workflows and SEO systems.

View My Services

Keep reading