§The idea
Two paths reach the server and they never meet. You arrive through a private mesh network. Visitors arrive through Cloudflare. The provider firewall drops everything else, so there is no public SSH port to attack. Inside the box, Claude Code runs in a persistent terminal session and handles the day-to-day administration.
§Who this is for
Best for developers and technical founders who are comfortable in a terminal, want a real server for side projects or small production sites, and would rather not maintain one by hand.
Not recommended if you have never used SSH, if the server will hold customer data from day one, or if you cannot afford an hour of downtime while you learn. Step 9 closes public SSH permanently. If that sentence is unfamiliar, practise on a throwaway server first.
§Before you start
- ●A VPS with at least 4 GB of RAM, Ubuntu 20.04 or newer, x64 or ARM64 — Anthropic's stated minimum. The cheapest tier at most providers is 1–2 GB and will not work.
- ●A domain you control, with registrar access.
- ●A Claude plan that includes Claude Code: Pro, Max, Team, Enterprise, or a Console account. The free plan does not include access.
- ●Free-tier Cloudflare and Tailscale accounts.
- ●VPS, 4 GB RAM — €5–10/month
- ●Cloudflare, Tailscale, Termius — free tier
- ●Claude plan — existing subscription
- ●Total added — €5–10/month
§Phase 1 — Buy (15 min)
- 01Create the VPS on Hetzner or DigitalOcean. Ubuntu 24.04, 4 GB RAM or more. Enable automatic backups at checkout.
- 02Generate an SSH keypair in your client. SSH is the standard way to get a command line on a remote machine, and Termius can create the keypair directly. Paste the public half into the create-server form. Keys only, never passwords.
- 03Add the domain to Cloudflare on the free plan and switch nameservers at your registrar. Cloudflare sits in front of your site, handling DNS and filtering incoming traffic. Start this now; propagation can take hours.
- 01Install an SSH client and Tailscale on both laptop and phone, signed in to the same account. Tailscale creates a private network that links only your own devices.
§Phase 2 — Lock it down (20 min)
This phase contains the only step that can permanently cost you access. Read it through before starting.
Step 5 — connect and update.
ssh root@YOUR_SERVER_IP
apt update && apt upgrade -yStep 6 — install Tailscale on the server using the official script and authenticate it.
Step 7 — disable key expiry for the server in the Tailscale admin console. Skip this and the server drops off your network in ninety days, after every other route is closed.
Step 9 — in the provider firewall, delete every inbound rule, then allow port 443 from Cloudflare's published ranges only. Fetch the list rather than typing it. Tailscale keeps working because the tunnel is established outbound; optionally allow inbound UDP 41641 for direct rather than relayed connections.
If your provider does not offer a cloud firewall, apply the equivalent rules with ufw on the server itself, allowing inbound only on the tailscale0 interface plus 443 from Cloudflare ranges. A host firewall is easier to lock yourself out of, so the recovery console matters more in that case.
Step 10 — test from mobile data with Tailscale off.
Step 11 — add your phone's own public key to authorized_keys. One key per device, so losing a phone means revoking one key.
§Phase 3 — Install the agent (5 min)
Step 12 — install tmux and Claude Code. tmux keeps a terminal session alive on the server after you disconnect. The native installer needs no Node.js; other paths, including signed apt repositories, are in Anthropic's setup guide.
sudo apt install tmux
curl -fsSL https://claude.ai/install.sh | bashStep 13 — run it inside tmux so the session survives disconnects.
tmux new -s claude
claudeDetach with Ctrl-b then d.
§Phase 4 — Hand it over
Step 14 — write the handover document. Claude Code reads CLAUDE.md at the start of every session. Vague instructions produce a server that surprises you.
# Server operating rules
## Facts
- Provider, plan, region, OS version
- Live server. Real traffic depends on it.
- Preferred stack, and how I want progress reported
## Security model — INTENTIONAL, DO NOT "FIX"
- Public SSH is closed on purpose. Access is via Tailscale only.
Never open port 22 to the internet.
- Inbound is 443 from Cloudflare ranges only. Never widen it.
- New services bind to localhost or Tailscale. Never 0.0.0.0.
## Conventions
- One project per directory: /srv/http/<domain>
- One tmux session per project, named after the domain
- Secrets in environment files, never committed
## Standing rules
- Ask before anything destructive: deletions, package removal,
config overwrites, database drops
- Prefer read-only diagnostics first
- Append every incident and its fix to this file
## Escalate, do not decide
- Anything touching DNS, firewall, or backupsKeep permission prompts on. That confirmation is the safety model.
Step 15 — have the agent write that file before it builds anything.
Step 16 — backups before features. A nightly push to a private GitHub repository, excluding environment files and tokens.
Step 17 — then let it build.
Caddy with the Cloudflare DNS plugin fits this architecture because the usual certificate method needs port 80 open and you have closed it; the DNS-01 challenge issues certificates over the DNS API instead. Nginx works too, but you would configure the challenge yourself.
SQLite is a good default here: one file on disk, already covered by your backup, and no listening service to secure. PostgreSQL or MySQL are fine if you need them — bind them to localhost or the Tailscale interface only.
§If something breaks
- ●Cannot SSH after step 9 — use the provider's browser recovery console, re-open port 22 temporarily, and diagnose Tailscale from there.
- ●Tailscale shows the server offline — usually expired keys; check step 7. Reconnect over the console and run
tailscale upagain. - ●Certificate never issues — the DNS API token is missing edit permission on the zone, or is scoped to the wrong zone. Check the Caddy logs.
- ●Site times out for visitors — the DNS record is set to DNS-only instead of proxied, so traffic arrives from outside Cloudflare's ranges and the firewall drops it.
§Know the limits
Your tailnet is a single point of failure, so put multi-factor authentication on it. The agent holds real privileges on a live machine — run it as a sudo user, not root, and read commands before approving them. And this is one box: no failover, no high availability. Right for personal projects and small sites, wrong for infrastructure other people depend on.
§What you have now
A server that only you can reach for administration, that only Cloudflare can reach for traffic, and that documents its own rules to the agent running it. From here, everything is a conversation: open the session, say what you want, approve what it proposes.
§Resources
- 01Claude Code overview
- 02Claude Code setup guide
- 03Claude Code memory (CLAUDE.md)
- 04Claude Code permissions (IAM)
- 05Hetzner Cloud
- 06DigitalOcean
- 07Cloudflare
- 08Cloudflare full DNS setup
- 09Cloudflare IP ranges
- 10Cloudflare SSL — Full (strict)
- 11Tailscale
- 12Tailscale download
- 13Tailscale Linux install
- 14Tailscale key expiry
- 15Termius
- 16Caddy
- 17Caddy Cloudflare DNS plugin
- 18SQLite
Shipped it but want a second pair of eyes on your copy, DNS, or email deliverability? bitroot.club does a $0 launch review for anyone who followed this guide. →