A Computer You Can Afford to Lose
I run coding agents with the safety off, every day. Just not on my laptop. Notes from months of pushing the work to a small cloud box: where remote wins, where local still embarrasses it, and what broke along the way.
In Something Is Happening I retold the story of a developer who asked Claude to clean up a directory and watched it run rm -rf ~/. SSH keys, dotfiles, years of projects, zeroed before recovery was even a conversation. It wasn’t a one-off, either. The same summer, Replit’s agent deleted a production database and later ignored a code freeze, and Gemini CLI shredded a user’s files while chasing a move operation that never happened. I filed them all under cautionary tales and moved on.
I still run coding agents with exactly that level of access. --dangerously-skip-permissions, the flag whose name is doing all the work, every day. The agent acts without asking first.
So I moved the agents to a different computer.
01 // The Box
It’s called cc-remote, which tells you how much ceremony went into naming it. A small ARM server at Hetzner, four cores, 8 GB of RAM, in a datacenter in Nuremberg. €10.99 a month before VAT, including the public IPv4 address. Two nice coffees in Berlin.
It runs NixOS, so a git repository directly defines its packages, services, containers, firewall rules, backup schedules, and alert thresholds. About once a minute the box checks that repository. When something new lands, it rebuilds itself:
$ git push # that's it$ ssh agent@cc-remote 'journalctl -u cc-remote-deploy -f' # watch it landThe security model is blunt: the server’s IP address appears in no DNS record, and no application or TCP service is exposed to the public internet. One UDP port for Tailscale, ICMP so it can be pinged, and that’s the entire inbound surface, enforced by Hetzner’s cloud firewall outside the box, because Docker cheerfully routes published ports around the host firewall and I’d rather not rely on remembering that. I reach the machine over a private Tailscale network, or tailnet. The handful of small apps it hosts publicly reach the world outbound, through a Cloudflare Tunnel, almost all behind an identity check; the one exception brings its own accounts. To the internet at large, the machine doesn’t exist.
Inside it, an agent user runs Claude Code in persistent terminal sessions. I attach from the Mac, detach, attach again from my phone on the train. The session doesn’t care. Neither does the agent, which has been chewing on a refactor the whole time.
02 // Where the Cloud Wins
Blast radius. My laptop is a pet. It has my photos, my keys, my browser sessions, a decade of accumulated state I could not reconstruct from memory. The box is cattle, in the Bill Baker sense. Reinstalling NixOS takes about 5–10 minutes; bootstrapping the pull-deploy path and restoring data come after that. I have not timed the full recovery yet, which matters. Still, the machine’s configuration is declared in git and its state goes to nightly encrypted backups. The flag is still dangerous; the blast radius is smaller.
Persistence. An agent session on a laptop lives at the mercy of the lid. Sleep, undock, dead battery in a backpack, session gone mid-thought. The box has no lid. I’ve started a migration at my desk, checked on it from the phone over lunch, and reviewed the diff in the evening. The always-on property sounds mundane until you work with tools that measure tasks in hours instead of keystrokes. Then it becomes the whole point. The economics quietly flip too: a comparable four-core Codespace bills $0.36 an hour, or about $263 for an always-on month before storage. The box costs €10.99 before VAT. Managed cloud dev environments are priced for sessions; agents want residency.
Position. This one I didn’t anticipate. The box became a place. It now hosts PDF tools, phone notifications, webpage-change alerts, and shell-history sync between machines. Renovate lands non-major dependency bumps on its own once the CI checks pass. Backups run around 03:30. None of these justified a server on their own. All of them showed up because a server was already there.
Compounding. The box is also my Mac’s Linux build farm. Any aarch64-linux build the laptop needs gets shipped over SSH and built natively instead of emulated. And the observability stack ended up with a strange primary interface: metrics and logs flow into Grafana, but the way I actually query them is by asking Claude, which talks to Grafana over MCP. The agent runs on the box, watches the box, and occasionally repairs the box. There’s a recursion in there I try not to think about too hard.
03 // Where Local Still Wins
The box is the weakest computer I own.
The M1 Pro on my desk has ten fast cores and twice the memory; the box has four shared ARM vCPUs. Fine for builds and agents, a joke next to the laptop. Anything x86-only runs under QEMU emulation, which Docker politely calls “much slower” and which landed around 4–5x slower in one x86-on-ARM Docker benchmark. Local LLM inference is out entirely: a 70B model is a 43 GB artifact and the whole machine has 8. Even a photo service like Immich recommends the box’s entire spec for itself alone. Raw compute per euro, the laptop I already own wins and it isn’t close.
Platform locks don’t negotiate. Xcode runs on macOS, not Linux, so every hour of iOS work stays on the Mac, full stop. The tight loops stay local too: UI work where I want the change on screen before my finger leaves the key does not want a network round-trip in the middle, however small. Gitpod’s own engineers, after six years of running cloud dev environments on Kubernetes, described the workload as needing burst CPU “within a few 100ms” or the terminal starts to feel like typing through syrup.
And the cloud is, definitionally, someone else’s computer plus a network path you don’t control.
A distributed system is one in which the failure of a computer you didn’t even know existed can render your own computer unusable.
Lamport meant it as a complaint, and it doubles as a checklist. If Tailscale, Hetzner, or the tunnel has a bad day, the path to my machine breaks. Each dependency is individually reliable and collectively a longer chain than “laptop, on desk.” The offline case is narrower than it sounds, though. Claude Code phones Anthropic’s API no matter where it runs, so the agent dies with the network either way. What offline local buys you is everything else: the editor, the compiler, the tests, the ability to keep working like it’s 2019.
There’s a subtler risk than outages: the platform itself moving under you. Gitpod left Kubernetes, renamed itself Ona, sunset its classic product with manual migration of secrets and workspaces, and was acquired by OpenAI, all in under two years. Nothing scandalous, companies pivot. My box is a commodity VPS wearing a git repo. If Hetzner annoys me, the NixOS description moves; only the provider-specific Terraform layer needs replacing. That is the part of cloud I’d defend anywhere: rent the metal, own the description.
Long-running, reattachable agent sessions and self-hosted services: the box. iOS, UI loops, heavy builds, and offline work: the laptop. Public-repo tasks that need neither my files nor my tailnet increasingly go to a third place, Anthropic’s own cloud sessions. The laptop is now one of three places where work happens.
04 // What Broke
A full EFI partition made every system switch fail. NixOS keeps old system generations so you can roll back when something goes wrong, and each generation parked a kernel in /boot. My EFI partition was 512 MB. In August every switch failed at bootloader installation while the previous generation kept running. Kernels now live on the main filesystem, while the EFI partition holds only the bootloader. The decision that caused the failure was months old by the time it surfaced.
:latest hid a 22-month-old image. The container runtime will not re-pull a tag it already has, so the evergreen name hid a frozen image and gave me neither freshness nor reproducibility. Everything is now pinned to a digest, and a bot proposes bumps. Every deployed version is a line in a diff instead of a mystery on disk.
Auto-merge is only safe if merged ≠ live. Renovate merges dependency updates while I sleep. The box builds the new system, activates it without touching the boot loader, checks Tailscale, the tunnel, SSH, and the agent binaries, then runs switch to make the generation permanent. A separate preflight checks /boot space. Failure restores the previous generation, quarantines the bad revision so the loop does not retry it every minute, and pages my phone. The shape comes from GitOps; the healthcheck gate before switch is the best idea on the box.
I locked myself out with my own firewall rules. When I tightened the tailnet to deny-by-default, the first policy used my Gmail address where the ACL needed my GitHub identity. The server spent four minutes refusing its only user. I recovered from the Mac through Tailscale’s admin and API path, which did not depend on the ACL I had broken. Deny-by-default bites its author first.
A backup nobody restored is a hypothesis. The nightly restic job encrypts the data, sends it to object storage, and verifies a random 5% sample after each run. Google’s SRE book says it plainest: nobody wants backups, people want restores. My first restore drill is still pending, so I do not get to call this solved.
05 // The Uncomfortable Part
The identity gate on the box is not a sandbox. The agent user can sudo. For a human attached to a terminal, that’s a defensible trade, and the repo records it as exactly that: an accepted risk for one supervised operator. For an unattended agent, one hijacked session means the whole machine, so anything genuinely autonomous waits until an isolated user with no sudo, no Docker, and no secrets exists. Anthropic’s own docs draw the same line: the flag I lean on is blessed only inside an isolation boundary, and their sandboxing work argues an agent needs filesystem and network isolation, because an agent that can read your SSH keys and reach the internet can mail them anywhere.
The major asynchronous coding agents now run in isolated cloud environments: OpenAI’s Codex, Google’s Jules, GitHub’s coding agent, and Anthropic’s cloud sessions. E2B, Modal, Daytona, and Fly now offer isolated runtimes where agents can be wrong safely. The execution environment is part of the safety model, the part prompts cannot cover.
For now I supervise the sessions. Before I leave them running alone, two boring jobs come first: split the privileges and time a full restore onto a fresh server.
Further Reading
I Asked Claude to Wipe My Laptop
The cautionary tale: an agent, a misunderstood instruction, and rm -rf ~/ on a machine full of irreplaceable state.
Leslie Lamport on Distributed Systems
The 1987 email containing the most honest definition of a distributed system ever written.
The History of Pets vs Cattle
Randy Bias traces the analogy back to Bill Baker's SQL Server talk and explains how to use it without embarrassing yourself.
Making Claude Code More Secure and Autonomous
Anthropic's engineering case that agent autonomy requires filesystem and network isolation, not more permission prompts.
We Are Leaving Kubernetes
Gitpod's engineering post-mortem on why development environments are brutally stateful, interactive, and latency-sensitive.