# Claude Code on VPS: Full Setup to Code from Your Phone

Complete guide to setting up Claude Code on a private VPS with Tailscale security, Tmux persistence, and Caddy HTTPS – code from anywhere, even your phone.

**Published:** February 8, 2026
**Category:** Software Development
**Tags:** AI Development, Vibe Coding, Claude Code

![Claude Code on VPS: Full Setup to Code from Your Phone](/blog/claude-code-vps-setup/tunnel.png)

---

I wanted to code from my phone. Not "review PRs" or "edit a config file" – actually run Claude Code, iterate on features, and preview changes in the browser. Turns out a $7/mo VPS is all you need.

This guide covers the full stack: Hetzner VPS, Tailscale for zero-trust SSH, Tmux for persistent sessions, and Caddy for HTTPS dev server access. By the end you'll have a setup where you SSH in from your phone, attach a Tmux session with Claude Code already running, and preview your app at `https://dev.yourdomain.com`.

---

## Architecture Overview

```mermaid
graph TD
    A[📱 Phone / 💻 Desktop] -->|Tailscale VPN| B[🖥️ VPS]
    B --> C[Tmux Session: project1 — Claude Code]
    B --> D[Tmux Session: project2 — Claude Code]
    C -->|starts| E[Tmux Session: project1-dev — Dev Server]
    E -->|localhost:3000| F[Caddy HTTPS Reverse Proxy]
    F -->|dev.yourdomain.com| A
```

The setup has 5 moving parts:

- **Hetzner VPS** – cheap, fast, EU/US datacenters. The machine where everything runs
- **Tailscale** – mesh VPN. SSH traffic goes over encrypted Tailscale tunnel, never over public internet
- **Terminus** – SSH client for iOS/Android/desktop. Connects to VPS via Tailscale IP
- **Tmux** – terminal multiplexer. Sessions persist when you disconnect. Phone goes to sleep? Claude keeps working
- **Caddy** – reverse proxy with automatic HTTPS. Exposes `localhost:3000` at `https://dev.yourdomain.com`

---

## VPS Setup

Go to [Hetzner Cloud](https://www.hetzner.com/cloud/), create a project, spin up a server:

- **Location**: closest datacenter to you
- **Image**: Latest Ubuntu
- **Type**: min: CX22 (2 vCPU, 4GB RAM, IPv4) – ~$7/mo, enough for me, but you choose you. If you run big projects and lots of parallel Claude instances, i would advise you get more memory

Hetzner sends the root password to your email. SSH in as root:

```bash
ssh root@<your-server-ip>
```

You'll be prompted to change the root password immediately on first login.

### Non-Root User

Claude Code with `--dangerously-skip-permissions` refuses to run as root. Create a dedicated user:

```bash
adduser agent
usermod -aG sudo agent

# Copy your SSH key to the new user
mkdir -p /home/agent/.ssh
cp ~/.ssh/authorized_keys /home/agent/.ssh/
chown -R agent:agent /home/agent/.ssh
chmod 700 /home/agent/.ssh
chmod 600 /home/agent/.ssh/authorized_keys

# Switch to agent
su - agent
```

From now on, everything runs as `agent`.

### Claude Code

Install Claude Code early – it can help you configure everything else on the server (Tailscale, firewall, shell, tooling, etc.).

```bash
# Install Claude Code
curl -fsSL https://claude.ai/install.sh | bash

# First run – authenticates via browser
claude
```

Follow the auth link. Once authenticated, you can use Claude Code to help with the remaining setup steps – or follow the guide manually.

### Tailscale

[Tailscale](https://tailscale.com/) creates a zero-trust mesh VPN between your devices. Free for personal use (up to 100 devices). Every device gets a stable `100.x.x.x` IP that works everywhere.

Install on VPS:

```bash
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
```

Follow the auth URL to link the VPS to your Tailscale account. Note the `100.x.x.x` IP – that's your SSH target from now on.

Install Tailscale on your phone/desktop too. Once both devices are on the same Tailnet, they can reach each other directly.

### Firewall (ufw)

Lock down the VPS. Only expose what's needed.

`ufw` is just a friendly front-end for the kernel's netfilter firewall – it isn't included on many minimal/VPS base images, so install it first:

```bash
sudo apt update
sudo apt install -y ufw
```

On other distros: `sudo dnf install -y firewalld` on RHEL/Fedora/CentOS/Rocky/Alma (or install `ufw` from EPEL), `sudo apk add ufw` on Alpine. The walkthrough below assumes Ubuntu/Debian.

**Lockout warning**: the rule below allows SSH **only** over the `tailscale0` interface. If Tailscale isn't connected when you enable the firewall, you'll be locked out of the VPS. Before running `sudo ufw enable`:

1. Install `ufw` (above)
2. Confirm Tailscale is up: `tailscale status`
3. Open a second SSH session to the Tailscale IP (`100.x.x.x`) and keep it open – it's your safety net if a rule is wrong
4. Add the rules
5. Then enable the firewall

```bash
# Allow SSH only over Tailscale interface
sudo ufw allow in on tailscale0 to any port 22

# Allow HTTPS from anywhere (for Caddy dev server)
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable

# Verify
sudo ufw status
```

Result: SSH is only accessible via Tailscale tunnel. Port 443 is open for the HTTPS dev server. Everything else is blocked.

---

## Terminus SSH Client

[Terminus](https://termius.com/) is a cross-platform SSH client – works on iOS, Android, macOS, Windows. It's the best mobile SSH experience I've found.

Setup:
1. Install Terminus on your phone and/or desktop
2. Create a new host:
   - **Address**: your VPS Tailscale IP (`100.x.x.x`)
   - **Username**: `agent`
   - **Auth**: SSH key (import your private key)
3. Connect – you're in

---

## Tmux

This is the critical piece. Without Tmux, closing your SSH connection (or your phone going to sleep) kills whatever's running. With Tmux, processes keep running on the server – you just detach and reattach.

```bash
sudo apt install -y tmux
```

```mermaid
graph TD
    A[SSH into VPS] --> B[tmux new -s project1]
    B --> C[Claude Code running in Tmux session]
    C -->|Phone sleeps / SSH drops| D[Session persists on server]
    D -->|Reconnect SSH| E[tmux a -t project1]
    E --> C
```

### Essential Commands

```bash
# Create named session (name it after your project)
tmux new -s project1

# Detach from session (keeps it running)
# Ctrl+B, then D

# List sessions
tmux ls

# Attach to existing session
tmux a -t project1

# Kill a session
tmux kill-session -t project1
```

### Parallel Workflow

Use one tmux session per project. Claude Code starts the dev server itself (in a background tmux session or via its built-in tools), so you just need one session to interact with:

```bash
# Project 1: your main project
tmux new -s project1
cd ~/project1
claude

# Detach: Ctrl+B, D

# Project 2: another project
tmux new -s project2
cd ~/project2
claude

# Detach: Ctrl+B, D

# Switch between projects:
tmux a -t project1
tmux a -t project2
```

Each project gets its own session. Claude handles dev servers, builds, and other processes within the session. Switch between projects as needed – everything survives SSH disconnects.

> **Parallel changes on the same repo?** Use [git worktrees](https://git-scm.com/docs/git-worktree). Worktrees let you check out multiple branches of the same repo into separate directories, so two Claude instances can work on different features simultaneously without conflicts:
>
> ```bash
> cd ~/project1
> git worktree add ../project1-feature-b feature-b
> # Now run a separate tmux session for the worktree
> tmux new -s project1-feature-b
> cd ~/project1-feature-b
> claude
> ```

### Configuration

Optional but recommended `.tmux.conf`:

`~/.tmux.conf`
```bash
# Mouse support (useful for scrolling)
set -g mouse on
```

Reload with `tmux source-file ~/.tmux.conf`.

---

## Shell & Tooling

Install the tools you'll need on the VPS. Here's what I'd recommend:

- **zsh** + oh-my-zsh – better shell experience, autocompletions, plugins
- **git** – version control, obviously
- **gh** (GitHub CLI) – create PRs, open issues, review checks, all from the terminal
- **docker** – if you run database instances, Redis, or other services locally
- **nvim** – if you want to edit files in vim mode directly on the server
- **Dev runtimes** – depends on your stack: bun, nvm (Node), python, uv, go, etc.

Once your shell is set up, add this alias to your `.zshrc`:

`~/.zshrc`
```bash
alias cc='claude --dangerously-skip-permissions'
```

This runs Claude Code in fully autonomous mode – no approval prompts for commands or file changes. Much better for async workflows where you kick off a task and come back later. But be cautious: in this mode Claude can do anything without asking, including pushing to main, running database migrations, deleting files, etc. Use it when you trust the task scope.

---

## Project Config

Set up `~/.claude/CLAUDE.md` so Claude Code understands your preferences from the start. This global config applies to all projects:

`~/.claude/CLAUDE.md`
````markdown
# Claude Code Rules

## Environment
- **Platform**: VPS accessed via SSH from multiple devices
- **Shell**: zsh
- **Session management**: tmux (always assume running inside tmux)

## Communication Style
- Concise and direct response. Sacrifice grammar for the sake of concision.
- Minimal explanation unless asked
- No emojis

## Tmux Workflow
Since this is a VPS environment with SSH access from multiple devices, use tmux sessions for process isolation:

- **Long-running processes** (dev servers, watchers, builds): Create new tmux sessions
  ```bash
  tmux new-session -d -s <name> '<command>'
  ```
- **Naming convention**: `{project}-{purpose}` (e.g., `myapp-dev`, `api-build`) - prefix with repo/project name to avoid collisions across parallel instances
- **Checking output**: Use `tmux capture-pane -t <session> -p` to read session output
- **Listing sessions**: `tmux list-sessions`

This allows reconnecting from any device and finding all processes still running in their respective sessions.

## Tools Available
- git, gh (GitHub CLI)
- Standard Unix tools
- oh-my-zsh

## Plan Mode

- Make the plan extremely concise. Sacrifice grammar for the sake of concision.
- At the end of each plan, give me a list of unresolved questions to answer, if any.
````

> You can adjust Tools Available section to include your coding environment like Bun, UV, etc.

The key VPS-specific addition is the **Tmux Workflow** section – it tells Claude Code to spin up long-running processes in separate tmux sessions instead of blocking the current terminal. This way Claude creates `myapp-dev` sessions for dev servers, and you can check their output or reattach from any device.

You can also add per-project `CLAUDE.md` files in each repo root for project-specific commands, architecture notes, and patterns. Claude Code reads both files automatically on startup. The easiest way to call /init inside Claude Code running in the project folder.

---

## HTTPS Dev Server

Problem: Claude Code starts `bun dev` on `localhost:3000`, but you can't hit localhost from your phone browser. Solution: Caddy reverse proxy with automatic SSL.

### DNS Setup

In your domain's DNS settings, add an A record:

`DNS Record`
```text
Type: A
Name: dev
Value: <your-vps-public-ip>
TTL: 300
```

This points `dev.yourdomain.com` to your VPS public IP.

### Caddy Setup

[Caddy](https://caddyserver.com/) handles HTTPS automatically – it provisions Let's Encrypt certificates with zero config.

```bash
# Install Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
```

Configure the Caddyfile:

`/etc/caddy/Caddyfile`
```text
dev.yourdomain.com {
    reverse_proxy localhost:3000
}
```

That's the entire config. Caddy auto-provisions SSL and reverse-proxies to your dev server.

```bash
# Restart Caddy to pick up changes
sudo systemctl restart caddy

# Check status
sudo systemctl status caddy
```

Now when Claude starts `bun dev` in a Tmux session, open `https://dev.yourdomain.com` on your phone – live preview of your app with hot reload.

**Security note**: only port 443 is publicly exposed (for the HTTPS dev server). SSH stays locked behind Tailscale. If you don't need public HTTPS access, you can skip Caddy entirely and access the dev server over Tailscale too (`http://100.x.x.x:3000`).

---

## Full Workflow

End-to-end, here's what coding from your phone looks like:

1. **Open Terminus** on your phone
2. **SSH into VPS** via Tailscale IP
3. **Attach Tmux session**: `tmux a -t project1`
4. **Talk to Claude Code** – describe the feature, fix, or change
5. You can close the app and break the connection. Claude edits files, runs commands, iterates
6. **Preview changes**: open `https://dev.yourdomain.com` in phone browser
7. Sessions keep running – come back anytime from any device

You can also run this from desktop with the same setup. The VPS becomes your persistent dev environment that you access from anywhere.

---

## Telegram Bot Interface

I've been building a [Telegram bot](https://github.com/Mark-Life/telegram-claude) that acts as a chat interface to Claude Code on the VPS. Instead of opening an SSH client, you just message the bot from Telegram – it spawns Claude Code in the selected project directory and streams results back.

```mermaid
graph LR
    A[📱 Telegram] -->|message / voice| B[Bot on VPS]
    B -->|spawns| C[Claude Code]
    C -->|streams output| B
    B -->|formatted response| A
```

Key features:
- **Project switching** – select which project directory to work in via inline keyboard
- **Voice messages** – record a voice note describing what you want, the bot transcribes it (via Groq Whisper) and feeds it to Claude Code
- **Streaming responses** – live-updated Telegram messages as Claude works
- **Conversation continuity** – follow-up messages reuse the same Claude session
- **Cost tracking** – each response shows token cost and duration

The workflow becomes: open Telegram, pick a project, describe a feature or fix (type or voice), and Claude Code does the work – edits files, runs commands, creates PRs. You get the output streamed back as Telegram messages.

It's built with TypeScript, Bun, and [grammY](https://grammy.dev/). The project is [open source](https://github.com/Mark-Life/telegram-claude) – still early and has some rough edges, but I already use it daily for real work. Voice messages in particular make it feel like you're just talking to your dev environment.

---

## Tips & Troubleshooting

**Claude Code is slow / OOM**
- 4GB RAM is tight if the project is large. Upgrade to CX32 (8GB) if needed
- Close unused Tmux sessions to free memory

**SSH connection drops frequently**
- Add to your SSH config:

`~/.ssh/config`
```text
# ~/.ssh/config
Host vps
  HostName 100.x.x.x
  User agent
  ServerAliveInterval 60
  ServerAliveCountMax 3
```

**Caddy not getting SSL cert**
- Verify DNS A record points to VPS public IP
- Check port 443 is open: `sudo ufw status`
- Check Caddy logs: `sudo journalctl -u caddy`

**Tmux session lost scroll history**
- Increase `history-limit` in `.tmux.conf`
- Use `Ctrl+B, [` to enter scroll mode, `q` to exit

**Phone keyboard tips**
- Terminus supports custom key mappings – map Ctrl, Escape, etc.
- Use Tmux prefix `Ctrl+A` (easier to reach than `Ctrl+B` on phone keyboards)

**Security checklist**
- SSH accessible only via Tailscale (never on public IP)
- Keep system updated: `sudo apt update && sudo apt upgrade`
- Tailscale ACLs for extra lockdown if sharing the Tailnet

---

## FAQ

### How much does it cost to run Claude Code on a VPS?

~$7/mo for a Hetzner CX22 VPS + Claude Code subscription ($20/mo for Pro, $100/mo for Max, $200/mo for 10xMax). Tailscale is free for personal use. Total: $27–207/mo depending on your Claude plan.

### Can I run Claude Code on AWS, DigitalOcean, or Linode instead of Hetzner?

Yes. Any Ubuntu VPS with SSH access works. I use Hetzner personally, but DigitalOcean, Linode, and Vultr all work the same way. AWS EC2 works too but is more complex to set up and usually more expensive for equivalent specs.

### Do I need Tailscale for VPS SSH, or is regular SSH enough?

I strongly recommend Tailscale. Exposing SSH on a public IP – even with key auth – means your server is one unpatched Ubuntu vulnerability away from being compromised. With Tailscale, the SSH port is never exposed to the public internet. There's nothing to scan, nothing to brute-force, no attack surface. You also get stable IPs across networks (home WiFi, mobile data, coffee shop) without port forwarding or dynamic DNS. Tailscale + ufw firewall is the baseline I'd consider safe for a development VPS.

### What VPS specs do I need for Claude Code? (CPU, RAM, storage)

2 vCPU / 4GB RAM handles one Claude Code instance and a dev server fine for small-to-medium projects. For larger projects or running multiple Claude instances in parallel, go 4 vCPU / 8GB RAM. Disk is rarely the bottleneck – 40GB is plenty unless you're running Docker images.

### Can I use VS Code Remote SSH with Claude Code?

Yes, VS Code Remote SSH works over Tailscale. But you lose the main benefit – working from your phone. The terminal-only workflow is intentional: Claude Code does the editing, you just direct it. If you prefer visual editing, VS Code Remote SSH + Cursor is a valid alternative for desktop.

### Is Claude Code `--dangerously-skip-permissions` safe to use?

I run everything with `--dangerously-skip-permissions`. The reason it works for me: this is a VPS, not my personal laptop. If Claude somehow destroys the environment, I don't lose anything irreplaceable – only project files (backed by git) and dev-only environment variables. No production credentials, no personal data.

That said, in this mode Claude can theoretically run `rm -rf`, `git push --force`, or drop a database without asking. To mitigate this, Claude Code supports [hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) – shell scripts that intercept commands before execution. You can set up safeguards like: block `rm -rf /` patterns, prevent `git push --force` to main, or enforce `drizzle-kit migrate` instead of `drizzle-kit push`. Hooks give you the speed of autonomous mode with guardrails where they matter.

### How to set up a shared VPS for multiple developers with Claude Code?

Create separate Linux users, each with their own home directory, tmux sessions, and Claude Code auth. Tailscale ACLs can restrict who can SSH in. Each user's projects stay isolated.

---

## Links

- Blog post: https://andrey-markin.com/blog/claude-code-vps-setup
- All blog posts: https://andrey-markin.com/blog
- Contact: https://andrey-markin.com/#contact
