Storage for agentsChapter 2 of 6
The obvious ways to build it, and where each one breaks
The short answer
Teams that build storage for agents themselves tend to climb the same ladder, from the sandbox’s own disk to uploads to S3, a mounted bucket, a network volume, NFS, and JuiceFS. Each rung fixes the failure of the one before it and meets a new one. Each puts the authoritative copy of the workspace in one place, and that place decides what breaks.
Hunter LeathFounder & CEO, Archil
15 min readChecked November 2026Read as Markdown
Every team that gives its agents a workspace starts with the storage it already has. Most then climb the same ladder. Each rung is a sensible answer to the rung before it, and each breaks somewhere new.
To compare the rungs fairly, this chapter runs one day through each of them. An agent clones a repository and runs npm ci. It commits, fsync() returns, and then the host it ran on is lost. A sandbox on a second machine picks up the work, forks three attempts at the next change, and then the workspace sits untouched for a week. The work outlives the machine explains why that day is ordinary for an agent.
Where we have measurements, the numbers are ours, and each section says what we measured on. Where we don’t, the chapter says what the vendor documents, and doesn’t guess.
The sandbox’s own disk§
The first workspace is the disk the sandbox came with. It’s the best storage on the ladder for the first half of the morning. Every tool works, every call is answered by the kernel on the same machine, and nothing later on the ladder is faster. Every other rung is measured against it.
It fails at the day’s third step. A sandbox runs on whichever host has room, so its disk is that host’s disk. Data on the host’s own drives doesn’t survive a retirement, a stop, or a termination.From AWS’s page on instance store data persistence. The work outlives the machine covers how hosts are retired. The clone, the install, and the commit go with it, and the sandbox that replaces it starts on an empty disk.
A sandbox on host 1 clones a repository, installs its packages, and commits, all on its own disk. Host 1 is retired, and the disk goes with it. The sandbox starts again on host 2 with an empty disk, so the clone, the install, and the commit are gone.
Even without a failure, the disk lasts only while the sandbox runs. An idle sandbox with 2 vCPUs and 4 GiB costs about $0.17 an hour at E2B’s list price.E2B bills $0.000014 per vCPU-second and $0.0000045 per GiB-second (E2B pricing). Keeping one up through a week of idling, so that its disk survives, costs about as much as storing a 5 GiB workspace in S3 Standard for 20 years.
An upload when the agent stops§
The second rung keeps the speed and adds a copy. The agent still works on the sandbox’s disk. The workspace goes to S3 at the end of each session as a tarball, or every few minutes with aws s3 sync or rclone. S3 stores objects across at least three availability zones, and a week of idling costs almost nothing.From AWS’s page on S3 data protection. Products sold to agent builders polish this design. Fly.io writes a Sprite’s data “to fast NVMe storage” and backs it up “to durable object storage” when the Sprite goes idle. Vercel describes its Drives as having “NVMe speed writes.”From the Sprites overview and Vercel’s Drives documentation.
The failure is the gap between a write and its upload. fsync() returns once the local drive has the data, and the host can be lost before the copy is made. We measured that gap on Vercel Drives. In 21 of 21 snapshots taken the moment fsync() returned, the file wasn’t there.
A sandbox wrote a 4 KiB file and a 64 MiB file, called fsync() on each, and synced the directory. After a delay, a new sandbox read the Drive through a snapshot or a fork. Taken the moment fsync() returned, 10 of 10 snapshots, 11 of 11 snapshots taken while the writer kept writing, and 11 of 11 forks were missing the file. At 1 second, every snapshot and fork still lacked it. At 30 seconds, 7 of 10 snapshots and 9 of 10 forks lacked it, and 4 of 10 snapshots taken while the writer kept writing held a 64 MiB file whose last 12 to 28 MiB read as zeros. From 60 seconds on, the file was intact in every trial but one. An Archil disk mounted in a new sandbox the moment fsync() returned had the file in 20 of 20 trials.
Drives and Sprites are reasonable trades for speed. But a database that tells a user their transaction committed has called fsync() and believed the answer. For more on where that answer comes from, see When is a write safe?
The rest of the day costs copies. The second machine downloads the last upload before it can start, and loses whatever came after it. Three attempts mean three downloads. A tarball keeps modes and links but moves the whole workspace every time. A sync tool moves only what changed, but as one object per file, which can’t hold everything a file can, as A file system on an object store explains.
Every team that builds this rung patches it, and each patch is sensible. The following list shows where the common ones lead:
- Upload more often. Syncing every minute instead of at the end shrinks the gap, but it never closes it. A busy host always has writes in flight when it dies, and the program was already told they were safe. In our Drive test, 7 of 10 snapshots taken 30 seconds after
fsync()were still missing the file. - Leave out
node_modulesand the build output. The uploads shrink, and every new machine reinstalls before it can work. Thenpm ciin our measurements took 10.7 s on a local disk, paid again on every start. - Snapshot with
tarinstead of syncing. A snapshot is consistent and keeps modes and links, but it moves the whole workspace each time. So it runs less often, and the gap grows. - Make the sync safe to run.
rclone syncmakes the destination match the source, “including deleting files if necessary,” and its documentation warns that “this can cause data loss.” If a restore fails quietly and the next upload runs from a nearly empty workspace, the good copy is what gets deleted.From the rclone sync documentation. - Lock the workspace so that two machines can’t both write it. Now you run a lock service, and you have to decide what happens to a lock whose holder’s host was retired. Chapter 3 is about that decision.
Follow the patches far enough and they meet at one requirement. The copy has to be made before fsync() returns, on every write, and the next machine has to start without a download. That isn’t a sync job anymore. It’s a file system whose storage lives somewhere else, which is the next rung.
A bucket mounted as a file system§
If the gap is the problem, remove it and write straight to the bucket. Mountpoint for Amazon S3 and s3fs mount a bucket so that each file is an object. Mountpoint uploads a file before fsync() returns, so a lost host takes nothing with it. Any machine can mount the same bucket, and every file is an ordinary object that other tools can read. In our measurements, a second machine saw a new file in under a third of a second without being told to look.
It fails at the first step of the day. S3 can’t rename an object, and git depends on renames. It publishes every change by writing a lock file and renaming it over the original. npm links package binaries with symbolic links. Mountpoint supports neither rename on a general purpose bucket nor symbolic links, and its README says not to use it for a Git repository. s3fs fakes a rename with a copy and a delete, so git runs, but its README lists atomic renames among what it can’t provide.From the Mountpoint README, its semantics document, and the s3fs README.
Five commands and the call each one depends on. git commit renames index.lock over index, and without an atomic rename git can’t update its index. SQLite writes a 4 KiB page at an offset in the database file, and without in-place writes each commit rewrites the whole file. npm install creates symbolic links in node_modules/.bin, and without symlinks the install fails. pnpm install hard-links files from its store into node_modules, and without hard links every file is copied. git checkout sets the executable bit on build.sh, and without mode bits ./build.sh fails with Permission denied.
Where it does work, every small file pays for a round trip to S3. Creating a 4 KiB file and syncing it took 244 ms on Mountpoint on our EC2 host. A mounted bucket suits training data that is written once and read many times. An agent’s workspace is the opposite.
A network volume§
The fourth rung goes back to a real disk, one that lives on the network. To the kernel, an EBS volume is a block device with ext4 on it, so git, npm, and SQLite run at local-disk speed. The local disk in our benchmarks is in fact an EBS gp3 volume. And the volume outlives the machine: “EBS volumes persist independently from the running life of an EC2 instance,” and EBS replicates each one within its availability zone.From the AWS pages on Amazon EBS volumes and EBS features. It’s the first rung that runs the whole morning and keeps the commit.
It breaks at the handoff. The file system on the volume keeps its map of blocks in one kernel’s memory, so the volume attaches to one machine at a time. And “the volume and instance must be in the same Availability Zone.” Taking it from a failed instance means a forced detach, which AWS calls a “last resort.” The instance “doesn’t get an opportunity to flush file system caches or file system metadata,” so you check the file system before you use it.From the AWS pages on attaching and detaching a volume. Multi-Attach lets an io1 or io2 volume attach to several instances in one zone, but ext4 and XFS aren’t built for that (Multi-Attach).
Machine A commits to an EBS volume in availability zone a, and then machine A is lost. The volume survives. Machine B, in the same zone, force-detaches the volume, attaches it, and checks the file system, and the commit is there. Machine C, in zone b, can’t attach the volume at all.
The rest of the day follows from the same fact. Three attempts mean a snapshot and three new volumes, and a volume restored from a snapshot fetches its blocks from S3 as they’re read, so it starts slow.From AWS’s Amazon EBS snapshots and Initialize Amazon EBS volumes pages. And a volume bills the size you gave it, idle or not. A 20 GiB volume holding a 5 GiB workspace bills four times what it holds, and EBS can grow a volume but can’t shrink it.
A shared file system over NFS§
The fifth rung lets every machine mount the same files at once. On Amazon EFS, any number of machines mount one file system over NFS. A Regional file system stores a write across availability zones when the program calls fsync() or closes the file, and you pay for what you store.From the AWS page on EFS features. The host can be lost, and the second machine mounts the file system and finds the commit. It’s the first rung that runs the morning’s tools and gets through the middle of the day without a forced detach or a download.
It breaks on the morning’s small files. When two machines create the same name, only the server can decide which one wins. So an NFS client waits for the server on every create, every change of mode, and every close. A small file costs about four round trips. For how those calls add up, see Anatomy of a file create. Amazon S3 Files, which serves a bucket over NFS, shows the cost on the install.
npm ci of a Next.js app writes 18,602 files. On one EC2 host, timed until every write was durable, it took 10.7 s on the instance’s own EBS volume, 12.4 s on Archil, and 71.4 s on Amazon S3 Files. The animation plays the three runs side by side, 10 times faster than they ran.
The install takes almost seven times as long over NFS as on the volume. EFS itself took 86.3 s for npm ci of a create-next-app project in a separate benchmark, 11 times as long as Archil in the same run.That benchmark stopped the clock when each command returned. For the hosts, versions, and commands behind both benchmarks, see File systems on one host. NFS clients also cache what they’ve seen, so a second machine sees a new file only when its cached copy of the directory expires. Archil’s clients do the same by default.
We created a file on one client and measured how long a second client took to see it. Archil, when the reader runs archil invalidate-cache first: about 70 ms. Mountpoint for Amazon S3: 284 ms, with no metadata cache by default. A Daytona Volume: about 360 ms, the median of 10 runs. Amazon S3 Files: 30 s, the length of its NFS directory attribute cache. Archil with default client caching: 30 s, its client cache expiry. A Modal Volume: not within 60 s, because a running sandbox sees another sandbox’s commit only after it reloads the Volume.
Forks are the last cost on this rung. EFS has no clone, so each attempt copies every file over NFS and pays the round trips of the install again.
JuiceFS, and a database you run§
The sixth rung stops asking one system to do everything. JuiceFS keeps names, directories, modes, links, and the map from each file to its blocks in a metadata database that you run, such as Redis or PostgreSQL. It keeps file contents as numbered blocks in your bucket.From the JuiceFS architecture page. The Community Edition is Apache 2.0. A rename changes one row, a write in the middle of a file uploads only the new bytes, and any machine can mount it. juicefs clone copies only metadata, so forks share blocks. It’s the most complete of the do-it-yourself designs.
A machine running the JuiceFS client commits. The names, modes, and map of blocks go to a metadata database you run, such as Redis or PostgreSQL, which you have to back up, fail over, and size. The file contents go to your bucket as numbered blocks. Lose the database, and the bucket holds blocks with no names.
The cost is the database, which you now operate, and the file system is only as safe as it is. JuiceFS’s own guide to Redis says the default log sync means “you may lose the writes from the last second.” It also says failover “does not guarantee that acknowledged writes are retained during failures,” and “Redis data needs to be backed up regularly.”From JuiceFS’s Redis best practices. Lose the database, and JuiceFS’s docs say “you cannot find the original file directly in the object storage.”
The database also sits in the path of every small file. With Redis on ElastiCache in the same availability zone, our npm ci took 161.6 s with JuiceFS’s defaults, 15 times as long as on the volume. Its write-back mode is faster because it stages blocks on local disk and uploads them later. Its docs warn that “if write cache data suffers loss before upload is complete, file data is lost forever.”From JuiceFS’s guide to cache. Our setup is in the file system benchmark. That is the second rung’s gap, back again.
One day on every rung§
Run the day yourself. Pick a design, and each step shows whether the workspace comes through it and how long it took where we measured it. The idle week is priced at list price for a workspace holding 5 GiB. Where we have no measurement, the step says what the vendor documents instead.
- 09:00Clone a repository: works, works, 15.5 s.
Django, from a local mirror, timed until durable. Measured on an EC2 instance’s EBS gp3 volume, our stand-in for a local disk. Source: measured.
- 09:01Run npm ci: works, works, 10.7 s.
A Next.js app, 18,602 files, from a warm npm cache, on the same volume. Source: measured.
- 11:40Commit, fsync() returns, the host is lost: fails, lost.
The workspace was on the host’s own disk. The commit, the install, and the clone go with the host. Source: documented.
- 11:45A second machine picks up the work: fails, nothing to pick up.
The new sandbox starts on an empty disk, and the agent starts the day over. Source: documented.
- 13:00Fork three attempts: works with a cost, copies, one machine.
Three
cp -acopies on the same disk, each as large as the workspace. A btrfs or ZFS disk clones in moments, but only on this machine. Source: documented. - Day 2–8Sit idle for a week: works with a cost, the machine bills, $28.
To keep the disk, keep the sandbox running: 168 idle hours of a 2 vCPU, 4 GiB sandbox at E2B’s list price. Source: list price.
Sandbox disk. Fast all morning. When the host goes, the workspace goes with it. 2 clean · 2 at a cost · 2 failed
Times are measured where marked; prices are list prices for a workspace holding 5 GiB.
One workspace through one day on seven designs, judged step by step: clone a repository, run npm ci, lose the host after fsync() returns, hand off to a second machine, fork three attempts, and sit idle for a week. Times are our measurements, and idle costs are for a workspace holding 5 GiB at us-east-1 list prices.
On the sandbox’s disk, the clone took 15.5 s and the install 10.7 s, measured on an EBS gp3 volume. The host loss takes the workspace, the second machine starts empty, forks are local copies, and keeping the sandbox up through the idle week costs about $28.
Uploading to S3 works at the same local speed, but the host loss takes everything since the last upload. The second machine and each fork download the last upload, and the idle week costs $0.03 in S3 Standard.
On a mounted bucket, the clone and the install fail, because Mountpoint can’t rename on a general purpose bucket or create symbolic links. The commit survives the host, the second machine mounts the bucket in 299 ms, forks are full copies, and the idle week costs $0.03.
On a network volume, the clone and the install run at the same speeds as the sandbox’s disk. The volume survives the host, but the handoff needs a forced detach, a machine in the same zone, and a file system check. Forks are a snapshot and three volumes that load lazily, and the idle week costs $0.37 for a 20 GiB volume.
On NFS, EFS took 81.5 s to clone React and 86.3 s to install a create-next-app project in a separate benchmark, where Archil took 9.8 s and 7.6 s. The commit survives, the handoff works, forks are full copies over NFS, and the idle week costs $0.35.
On JuiceFS with its defaults, the clone took 247.4 s and the install 161.6 s, or 29.2 s and 53.8 s in write-back mode. The commit is as safe as the metadata database, the handoff works, forks copy only metadata, and the idle week costs $0.03 in S3 plus the database.
On Archil, the clone took 19.8 s and the install 12.4 s. The commit survives. The lost client’s checkout doesn’t expire. Archil marks it orphaned, usually within a minute, and the second machine takes it over with --force, losing only writes that were never synced. It then mounts in 44 ms. Forks are one 13 ms checkpoint and three 33 ms branches at the median, and the idle week costs $0.03, billing the archive rate on every byte.
Every rung gets through part of the day. The fast ones lose the host or stay with one machine in one zone. The ones that share are slow on small files, can’t run git, or need a database you run.
Where the authoritative copy lives§
Each rung failed for its own reasons, but the reasons share a shape. Each design decides where the authoritative copy of the workspace lives, and which machine may answer a read or a write without asking anyone else. The following figure puts each rung where it keeps that copy.
Where each rung keeps the authoritative copy of the workspace. The sandbox’s disk keeps it on the machine: fast, and gone with the host. Uploading to S3 keeps it on the machine first and in the bucket later: fast, and it loses what wasn’t uploaded. A mounted bucket keeps it in the bucket: safe and shared, without rename or links. A network volume keeps it on a volume: safe and fast, for one machine in one zone. NFS keeps it on a server: safe and shared, with a round trip for each change. JuiceFS keeps the map in a database and the blocks in the bucket: complete, with a database to run.
A design that fixes the authority in one place gets part of the day, and climbing to the next rung only moves the authority somewhere else. The way off the ladder is to stop fixing it in one place: keep the truth remote and replicated, and lend the right to answer to whichever machine is doing the work. The idea is decades old, and Where the truth lives covers why it works and what building it takes.