shedix

Copying an agent's machine: disk clones and snapshots on Google Cloud, measured

Setting up a machine for an agent takes a while: the code, its dependencies, Docker images, and signing in to Claude Code, Codex and GitHub. When several agents should start from that same point, doing it all again for each one is wasted time. So shedix can copy a machine: one to five new machines from one machine's disk, with everything on it.

Copying a disk sounds like one API call. On Google Compute Engine it became two methods, a rate limit and a flush we didn't expect to need. Here's how it works, with the times we measured on 1 November.

Why copy instead of setting up again

When we traced an agent setting up a two-service project on a fresh machine, the session took 12 minutes 20 seconds, and the dependency installs alone took 3 minutes 39 seconds on an S. A copy skips that: installed packages, build output and sign-ins are already on the disk.

We considered copying only the agent's own folder, such as ~/.claude. That misses whatever else the agent set up: installed tools, data, other sign-ins. Copying the whole disk carries all of it.

One copy: a disk clone

For one copy, we clone the source's boot disk, then create a machine on the clone. In Compute Engine's REST API that's two calls, shown here without the parts that don't matter:

POST /compute/v1/projects/PROJECT/zones/ZONE/disks
{"name": "COPY", "sourceDisk": "zones/ZONE/disks/SOURCE", "type": "zones/ZONE/diskTypes/pd-balanced"}

POST /compute/v1/projects/PROJECT/zones/ZONE/instances
{"name": "COPY", "disks": [{"boot": true, "autoDelete": true, "source": "zones/ZONE/disks/COPY"}], …}

Google's disk clone docs say a disk can be cloned while it's attached to a VM, and that a clone needs no pre-warming. In our first test, the clone took 6 seconds and creating the machine 13 more, so the copy call answers in about 20 seconds. In a live run, the call answered in 23 seconds and the copy was ready 15 seconds later.

The catch is a rate limit: one clone of a given disk every 30 seconds, and clones of that clone count against the same limit. We tried making several copies as a chain, each cloned from the one before, and Google refused with Too frequent operations from the source resource.

Several copies: a snapshot, and the wait

For two to five copies, we take one snapshot of the source's disk and create every copy's disk from it at once. Instant snapshots looked faster, but in our tests, restoring several disks from one instant snapshot hit the same kind of rate limit. So we use a standard snapshot, and it's the slow part:

Run Snapshot Copies ready
First test 110 s copies then created together in 60 s
Live run 1 m 48 s 3¼ minutes after the call
Live run 3 m 57 s over 5½ minutes after the call; one machine took over 2 minutes to create

Google's snapshot best practices agree that standard snapshots take longer to copy than clones or instant snapshots, and add limits: a disk can be snapshotted at most 6 times an hour, and one snapshot can make at most 6 new disks an hour in a zone. Five copies from one snapshot fit. The same page suggests two things we haven't tried: for many disks, make an image from the snapshot, since disks made from images aren't limited that way; and keep a disk's previous snapshot, so the next one reads only what changed. We delete copy snapshots after 15 minutes, so a later copy gets no head start.

Our first version held the API call open until every copy existed, and copying two machines held it for 210 seconds. That's too long for the callers we care about. Claude Code stops a command after two minutes by default (tools reference), an agent that retries makes more copies, and on Cloudflare Workers, work tied to a request may be cancelled once the caller disconnects. That could leave a snapshot and machines no record knows about.

Now the call starts the snapshot, records each copy as pending and answers in 14–16 seconds. Whatever next looks at a pending copy (a list, its page, a call waiting with ?wait=) creates its machine once the snapshot is ready, and so does a job that runs every 5 minutes. The same job deletes snapshots no copy still needs. A copy deleted while it waits never gets a machine, and one still waiting after 30 minutes is given up.

Write memory to disk before you copy

Early test copies were missing files written seconds before the copy. Linux keeps recent writes in memory and writes them out a little later, and a clone or snapshot only sees the disk. Google's docs say a snapshot of a running disk "might not capture pending writes that are in transit from memory to disk."

So before copying a running machine, we ask it to flush; a stopped machine's disk is already complete. Our API sets a metadata value on the machine to a fresh token. A small service on the machine waits for that value to change with the metadata server's wait-for-change feature, runs sync, and writes the token back as a guest attribute. The API waits up to 20 seconds for it, copies, and reports flushed: true or false. A sketch of the machine's side, with the key names simplified:

MD=http://metadata.google.internal/computeMetadata/v1
etag=NONE  # afterwards, the ETag header of the last answer, so no change is missed
# Blocks until the API sets a new value.
token=$(curl -s -H 'Metadata-Flavor: Google' "$MD/instance/attributes/flush-request?wait_for_change=true&last_etag=$etag")
sync
curl -s -X PUT -H 'Metadata-Flavor: Google' --data "$token" "$MD/instance/guest-attributes/flush/done"

The first version asked the machine's own web server, through its tunnel. A live test found the tunnel down while SSH still worked, so the request now goes through Google's metadata server, which every machine can reach.

A flush gives a crash-consistent copy, not an application-consistent one. A database running on the source is copied as if the machine had crashed at that moment, and Google notes such copies may need their journals replayed. We don't pause anything; the source keeps working while it's copied.

What comes along, and what doesn't

On 1 November we wrote stand-in sign-in files for 15 tools seconds before copying: Claude Code, Codex, gh, git credentials, Docker (the user's and root's), npm, AWS, gcloud, .netrc, an SSH key, Wrangler, Firebase, Vercel and Tailscale. On a clone and on two snapshot copies, every file had the same checksum, permissions and owner. Copies only ever go to the same person's machines.

Each copy gets its own name, public address, SSH host keys and the secret behind its web terminal and VS Code. Three things don't separate cleanly:

  • The machine ID. Copies share the source's /etc/machine-id. systemd's docs say each machine's ID should be unique; our images ship with it empty, but a copy comes from a machine that has already booted.
  • One-per-machine identities. Tailscale's docs say a cloned file system leaves two devices with the same Tailscale address until one is reinstalled. Our guide tells agents to sign such things out on a copy, or give each copy its own.
  • Running programs. Nothing that was running carries over, nor anything held only in memory, such as an ssh-agent's keys. Start dev servers and agents again on each copy.

We haven't yet used a real Claude Code sign-in on a source and its copy for a day. If a tool rotates its refresh token, one of the two may get signed out.

Memory snapshots and checkpoints elsewhere

Others copy more, and faster. CodeSandbox's SDK lists snapshot restores within 1 second and VM clones within 2 seconds on its microVM infrastructure. boxes.dev snapshots a template box's filesystem and memory, and starts each agent's devbox from it in seconds. In Thomas Ptacek's design post, Sprites' checkpoint and restore only move metadata, because a Sprite's data lives on object storage, and they're quick enough that Fly.io wants them used "like a git restore, not a system restore." Kurt Mackey's launch post shows a restore taking about a second.

A memory snapshot brings running programs along, which a disk copy can't. It also copies more of what should be unique. Firecracker's snapshot docs warn that restoring one snapshot more than once can leave identifiers, random number state and cryptographic tokens duplicated. Our copies have a small version of that problem, in the machine ID and the Tailscale node.

On Compute Engine, copying a disk costs under a minute for one copy and several minutes for more. Copy before you hand out work, not while agents wait on it.

To try it, use Copy on a machine's page, or shedix copy <name> --count 3 from a terminal. The call and its options are in the API docs, and the agents' guide tells an agent when to copy a machine and what to sign in to first.