Hetzner + Cloudflare + Claude Code

Step-by-step guide to setting up a remote dev environment

Architecture Overview

Loading diagram...
Pan and zoom to explore. Drag nodes to rearrange.

1 Hetzner VPS

A VPS (Virtual Private Server) is a remote computer in a data center that stays on 24/7. Hetzner is a German hosting provider with great prices. You'll rent one and use it as your always-on development machine.

Order a server

Go to Hetzner Cloud Console and create a new project. Add a server:

Initial SSH

SSH (Secure Shell) lets you control the remote server from your local terminal. Hetzner gives you an IP address after creation — use it to connect:

ssh root@<your-ip>

Create a user

Running as root (the admin account) is risky — a typo can break everything. Create a regular user for daily work, with sudo for when you need admin privileges:

adduser axel
usermod -aG sudo axel

# Copy SSH key
mkdir -p /home/axel/.ssh
cp ~/.ssh/authorized_keys /home/axel/.ssh/
chown -R axel:axel /home/axel/.ssh
chmod 700 /home/axel/.ssh
chmod 600 /home/axel/.ssh/authorized_keys

Firewall & basics

Update all installed packages, then enable the firewall. UFW (Uncomplicated Firewall) blocks all incoming traffic except what you explicitly allow — here, just SSH so you can still connect:

# As root
apt update && apt upgrade -y

ufw allow OpenSSH
ufw enable

# Disable root login so only your user can SSH in
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd
Tip: After disabling root login, verify you can SSH as your user before closing the root session.

2 Claude Code

Claude Code is an AI coding assistant that runs in your terminal. It can read your files, write code, run commands, and handle git — like pair-programming with an AI. You'll install it on the VPS so it's always available.

Install Claude Code

The native installer downloads the CLI binary and sets up automatic updates:

# Native installer (recommended — auto-updates, no Node.js needed)
curl -fsSL https://claude.ai/install.sh | bash
claude --version

Authenticate

# Start claude and follow the login prompt
claude

# Or log in explicitly
claude /login

Supports Claude Pro/Max subscriptions, Console API keys, or enterprise providers (Bedrock, Vertex, Foundry).

Tip: The native installer handles updates automatically. No need for nvm or npm.

tmux setup

tmux is a terminal multiplexer — it keeps your terminal sessions alive on the server even when you disconnect. Without it, closing your laptop would kill any running Claude session:

# Start a named session
tmux new -s claude

# Inside tmux, run claude
claude

# Detach: Ctrl+B, then D
# Reattach later:
tmux attach -t claude

3 Cloudflare

Cloudflare sits between users and your server. It provides DNS (translating domain names to IP addresses), a global edge network (serving content from 300+ locations worldwide), and tunnels (secure connections between your VPS and Cloudflare without opening ports).

Account & domain

To use Cloudflare, you add your domain and point its nameservers to Cloudflare. This means Cloudflare handles all DNS for that domain:

Install cloudflared

cloudflared is the client that runs on your VPS and establishes an encrypted tunnel to Cloudflare's edge. This lets you expose services (like SSH) without opening firewall ports:

# Add Cloudflare GPG key and repo
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list

# Install
sudo apt-get update && sudo apt-get install -y cloudflared
cloudflared --version

Authenticate cloudflared

cloudflared tunnel login
# Opens a URL — copy it to your browser and authorize

Create a tunnel

A tunnel is a persistent, encrypted connection from your VPS to Cloudflare. Traffic flows through it instead of directly to your server's IP:

# Create
cloudflared tunnel create my-tunnel

# Note the tunnel ID (UUID) from the output
# Config file is at ~/.cloudflared/config.yml

Configure the tunnel

The config file maps hostnames to local services. Here, requests to ssh.yourdomain.com get routed to your VPS's SSH server:

# ~/.cloudflared/config.yml
tunnel: <TUNNEL-ID>
credentials-file: /home/axel/.cloudflared/<TUNNEL-ID>.json

ingress:
  - hostname: ssh.yourdomain.com
    service: ssh://localhost:22
  - service: http_status:404

Run as systemd service

systemd manages background services on Linux. Installing cloudflared as a service means it starts automatically on boot and restarts if it crashes:

sudo cloudflared service install
sudo systemctl status cloudflared

DNS record

This creates a DNS record so that ssh.yourdomain.com routes through your tunnel:

cloudflared tunnel route dns my-tunnel ssh.yourdomain.com
Tip: Add Cloudflare Access in front of the SSH hostname for zero-trust authentication.

4 Workers

Cloudflare Workers are serverless functions that run on Cloudflare's edge network — your code runs in 300+ data centers worldwide, close to your users. No servers to manage, no scaling to worry about. You write code, deploy it, and Cloudflare handles the rest.

Install Node.js

Node.js is a JavaScript runtime. Wrangler (the Workers CLI) requires it. nvm (Node Version Manager) lets you install and switch between Node versions easily:

# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
source ~/.bashrc

# Install latest LTS
nvm install --lts

Install wrangler

Wrangler is Cloudflare's CLI tool for creating, testing, and deploying Workers:

npm install -g wrangler

Authenticate

Wrangler needs to know which Cloudflare account to deploy to. On a headless VPS (no browser), use API keys stored in a file you source before each command:

# Option A: Interactive (opens browser)
wrangler login

# Option B: API key (better for headless VPS)
# Create ~/.env.cloudflare:
export CLOUDFLARE_EMAIL="you@example.com"
export CLOUDFLARE_API_KEY="your-global-api-key"

# Source it before any wrangler command
source ~/.env.cloudflare
Note: Use the Global API Key (not an API Token) with CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY for wrangler on a headless VPS.

Create a worker

A Worker project is just a folder with a config file (wrangler.toml) and a TypeScript entry point. Hono is a lightweight web framework that makes routing easy:

mkdir -p ~/projects/my-worker && cd ~/projects/my-worker
npm init -y
npm install hono
npm install -D wrangler @cloudflare/workers-types typescript

wrangler.toml

This config file tells wrangler your worker's name, entry point, and what domain to serve it on:

name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-12-01"

routes = [
  { pattern = "app.yourdomain.com", custom_domain = true }
]

src/index.ts

import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello from Cloudflare Workers!'));

export default app;

Deploy

This uploads your code to Cloudflare's edge. Within seconds, it's live worldwide:

source ~/.env.cloudflare
npx wrangler deploy

5 Putting It Together

With everything set up, here's how the pieces connect. You SSH into the VPS, use Claude Code to build things, and deploy to Cloudflare's edge — all from the terminal.

The development loop

This is your daily workflow. SSH in, reattach to your tmux session where Claude is running, build something, and ship it:

# 1. SSH into VPS (through CF tunnel or direct)
ssh axel@ssh.yourdomain.com

# 2. Attach to tmux
tmux attach -t claude

# 3. Claude Code builds your worker
claude "Create a worker that does X"

# 4. Deploy
source ~/.env.cloudflare
cd ~/projects/my-worker
npm run build && npx wrangler deploy

Project structure pattern

~/projects/
  my-worker/
    src/
      index.ts       # Hono app
      page.ts        # HTML templates
      client/        # React/Vite client code (if needed)
    public/          # Vite build output (git-ignored)
    wrangler.toml
    package.json
    vite.config.ts   # If you have client-side JS

Useful aliases

# Add to ~/.bashrc
alias cfe='source ~/.env.cloudflare'
alias deploy='npm run build && npx wrangler deploy'
alias cc='claude'

Key things to remember