A Claude Code session doing real work — reading a codebase, executing a plan, running tests, fixing what they catch — can go forty minutes without needing me. My laptop is a terrible place for that session to live. It sleeps, it roams between networks, and it gets shoved in a bag mid-task.
So I moved the session somewhere that does none of those things. My development environment is now a $9 server in a Hetzner data center, and my laptop, iPad, and phone are just windows into it. I start a refactor at my desk, close the lid, and read the result an hour later on my phone.
The rig has five parts, each boring on its own:
- A small VPS that runs 24/7 and holds the repos, dotfiles, toolchains, and Claude Code.
- tmux, so sessions outlive the SSH connections that view them.
- SSH from every device — a terminal on the laptop, Termius on the phone and iPad.
tm, a one-line shell function that drops any device into the same session.- Remote Control, so the agent can ping my phone when it needs me.
Step 1: Rent the Box
My box is a small shared-vCPU server on Hetzner, about $9 a month. That sounds modest, and it is, but the box mostly runs a shell, git, and Claude Code. The inference happens on Anthropic's side. You're renting a desk, not a workshop.
Pick Ubuntu LTS and the region closest to you, since every keystroke makes that round trip. And add your SSH public key at creation time, so password auth never has to exist on the box at all.
Step 2: Lock It Down
A fresh public IPv4 address gets scanned within minutes. This box will hold your code and your agent's credentials, so harden it before you do anything else.
# as root: a sudo user that inherits your key
adduser chris
usermod -aG sudo chris
rsync --archive --chown=chris:chris ~/.ssh /home/chris
Then close the two doors the bots are knocking on. Put these in a drop-in file, not sshd_config itself: Ubuntu reads /etc/ssh/sshd_config.d/ first, sshd keeps the first value it sees, and a cloud-init file there can quietly turn password auth back on.
printf 'PasswordAuthentication no\nPermitRootLogin no\n' \
| sudo tee /etc/ssh/sshd_config.d/00-hardening.conf
sudo sshd -T | grep -Ei 'passwordauth|permitroot' # verify both say "no"
Restart with sudo systemctl restart ssh, and log in as the new user from a second terminal before closing the first. Never lock the only door you're holding open.
Add a firewall and automatic security patches:
sudo ufw allow OpenSSH
sudo ufw enable
sudo apt install unattended-upgrades
That's the floor. The better setup takes SSH off the public internet entirely with Tailscale. Install the Tailscale app on your client devices first, so you have something to test from:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
sudo ufw allow in on tailscale0
# confirm you can SSH in over the tailnet, then:
sudo ufw delete allow OpenSSH
Port 22 now answers only to machines on your tailnet. If Tailscale ever breaks, Hetzner's web console still gets you in.
Step 3: Set It Up Once
Dotfiles, shell, git config, toolchains, tmux (sudo apt install tmux). Every laptop I've owned put me through this ritual, and I'd discover three months later that something was missing at the worst possible moment. A persistent server ends the ritual. You do it once, and a new device needs nothing but an SSH client.
Then Claude Code, via the native installer:
curl -fsSL https://claude.ai/install.sh | bash
On a headless box, claude prints a login URL. Open it on whatever device is in your hand, approve, and paste the code back. The credentials live on the server, so every device you connect from is already logged in.
Step 4: One Function, tm
When an SSH connection drops, the shell gets SIGHUP and takes its children down with it. Your train goes into a tunnel, your agent goes with it. tmux breaks that chain: the session runs on the server, attached to nothing, and SSH is just a viewer that comes and goes.
The whole thing runs on one line in my shell config:
tm() { tmux new -As "${1:-main}"; }
-A attaches to the named session if it exists and creates it if it doesn't, which makes tm idempotent; ${1:-main} just saves typing. It means "take me to my workspace," regardless of which device asks or whether the workspace exists yet. tm on the laptop puts you in main. tm on the phone an hour later puts you in the same main — same scrollback, same agent mid-thought. Named sessions give you parallel workspaces for free: tm website for this blog, tm trading for the thing that loses me money.
Detaching is Ctrl-b d, or nothing at all. Close the app, lose signal, walk away. tmux can't tell a deliberate detach from a dropped connection, and that indifference is the entire feature. Flaky airport wifi and closing your laptop on purpose become the same non-event. Start Claude on a long task, walk off, and reattach from whatever's nearby to scroll through everything it did while you were gone.
Step 5: Put It in Your Pocket
Termius is my client on iPhone and iPad. Generate a key in the app, add the host, connect. I keep a separate key per device, so losing a phone means deleting one line from authorized_keys, not rotating everything.
Two settings worth finding immediately:
- The extra keyboard row. It puts
Esc,Ctrl,Tab, and arrows above the on-screen keyboard. On glass, this row is the difference between tmux being usable and tmux being a hostage situation. - Font size. Turn it down. A phone in landscape at a small font shows a surprisingly respectable amount of terminal.
Then type tm. The iPad with a keyboard case is the sleeper hit: with a terminal full screen, it stops being "coding on a tablet" and just becomes coding.
Step 6: Get Pinged
The weak spot so far: the agent hits a permission prompt while you're out, and it waits until you happen to check. Remote Control fixes that. Start Claude inside tmux with it on:
claude --rc
Or set "remoteControlAtStartup": true in ~/.claude/settings.json to turn it on for every session. Install the Claude app on your phone, sign in with the same account, then run /config and enable Push when actions required (permission prompts and questions) and Push when Claude decides (usually a finished task). It needs a Pro, Max, Team, or Enterprise login; API keys don't work.
The session connects out over HTTPS and opens no inbound ports, so it works fine behind the Tailscale lockdown. When the push arrives, you approve or answer right in the Claude app. It doesn't replace Termius: the app gives you the conversation, Termius gives you the shell.
Two caveats. While Remote Control is connected, Anthropic stores the session transcript on its servers; execution stays on your box. And anyone who gets into your claude.ai account can now steer an agent with a shell on your server. On Pro or Max, turn on Require trusted devices in your account settings so only enrolled devices can drive it.
What It's Actually Like
"I code from my phone" conjures someone typing function bodies on glass. That's not what happens. Typing code on a phone is miserable, and I don't do it.
What changed is that agentic coding made most of the work reading and deciding. The loop from Vibe Coding with Guardrails — research, plan, invite pushback, review the diff — is mostly conversation, and conversation is phone-shaped. The keyboard-shaped work happens when I'm at a keyboard; the judgment-shaped work happens wherever I am.
In practice: I kick off a task after breakfast, approve the plan from my phone while running errands, and read the diff twenty minutes later on the iPad. Small windows that used to be dead air are now enough to unblock an agent.
The Tradeoffs
- Cost. About $9 a month. I spend more than that on a single cocktail, so I've made my peace, but it's still a subscription.
- Latency. Every keystroke round-trips to the server. On the same continent it's imperceptible; across an ocean you'll feel it. If you're often on bad connections, mosh is worth the ten minutes: it uses SSH only to log in, then switches to its own UDP protocol with local echo and roaming. Over Tailscale, it needs no firewall changes.
- Your code lives on someone else's computer. For personal projects I'm comfortable with a hardened box at a reputable provider. Work code has a different owner, so check your employer's policy before cloning anything onto a personal server.
- Shelf life. Anthropic keeps shipping official ways to work away from your desk, and Remote Control already does part of this rig's job. But it needs a machine that stays on, and a $9 Linux box you fully control is a good one to give it.
My laptop still gets shoved in a bag mid-task. Now nothing stops when it does.