Running out of disk in the middle of a batch
Vercel doubled Sandbox storage to 64 GB on 11 September 2026. Disk is still the one resource that does not scale with your plan, and ENOSPC is not a fault a retry can fix.
On 11 September 2026 Vercel published a one-line changelog: every Vercel Sandbox now includes 64 GB of storage, up from 32 GB. It reads like a footnote. If you rasterise documents inside a sandbox before sending them to a model, it is the difference between a batch that finishes and a batch that dies two thirds of the way through — and, more usefully, it is a reason to go and look at the one resource in your pipeline that nobody has put a number on.
Every other resource in an AI pipeline degrades under pressure. Disk does not degrade. It works, and then it returns ENOSPC, and the work you already paid for is gone.
What a page actually costs on disk
Numbers first, because this argument only works with them. I took a six-page A4 PDF and rasterised it at 300 DPI — the resolution most OCR and vision pipelines settle on — in three formats, then measured what landed on disk:
uncompressed TIFF 26,152,344 bytes/page 24.94 MB
JPEG q85 760,828 bytes/page 0.73 MB
PNG 418,265 bytes/page 0.40 MB
Divide 64 GB by each of those:
TIFF 68,719,476,736 / 26,152,344 = 2,627 pages
JPEG 68,719,476,736 / 760,828 = 90,321 pages
PNG 68,719,476,736 / 418,265 = 164,296 pages
Two things in that table are worth stopping on.
The first is that JPEG is larger than PNG here, which is the opposite of what most people assume. This document is born-digital: sharp black text on flat white. PNG's lossless compression eats flat colour for breakfast, while JPEG spends bytes encoding ringing artefacts around every letterform. On a genuine scan — paper grain, sensor noise, a slight grey cast — the ordering reverses. Which means you cannot pick the format from first principles. You measure it on your documents.
The second is 2,627. A pipeline that writes uncompressed page images, which is exactly what you get if you hand a PDF to a rasteriser and take its default output, cannot process a 3,000-page batch inside a sandbox. Not slowly. At all.
Why doubling it is not the fix
Here is the part that made me want to write this. Vercel's published resource limits scale with your plan, except for one row:
| Plan | Max vCPUs | Max memory | Disk |
|---|---|---|---|
| Hobby | 4 | 8 GB | 64 GB |
| Pro | 8 | 16 GB | 64 GB |
| Enterprise | 32 | 64 GB | 64 GB |
CPU goes up 8×. Memory goes up 8×. Disk is flat. An Enterprise contract buys you exactly the same 64 GB that the free tier gets, and that 64 GB is not yours alone — the OS image, your dependencies, any model weights you baked in and the output you are producing all come out of the same pot.
So the ceiling is not a purchasing decision. There is no plan to upgrade to, no support ticket to raise. It is an architectural constraint, and doubling it bought the industry roughly one growth quarter of not thinking about it.
(One detail worth verifying rather than trusting me on: the changelog says the
increase covers managed images, custom images and the deprecated runtime
property, while the pricing docs — last updated 2026-09-02, nine days earlier —
still carve out 32 GB for sandboxes created with the deprecated runtimes. Check
which one your own code path gets before you size anything against it.)
The failure is not the kind retries fix
I gave a 120 MB volume to two versions of the same loop to watch this happen on a real filesystem rather than in a thought experiment. Each iteration writes one of those 26 MB page images, which stands in for the rasterise-then-call-the-model step. Real output:
volume free at start: 124522496 bytes; page image: 26152344 bytes
naive: died at page 4/8 with ENOSPC — 4 pages of model spend, nothing written back
guarded: stopped before page 4/8 — checkpoint written, 19906560 bytes still free, resume from page 4
The naive run is how most pipelines are written. It is not obviously wrong. It fails at page four with an exception, and because a sandbox filesystem is ephemeral, the four pages of results it had in hand die with the microVM.
Now price that. At 1,800 input and 400 output tokens per page on a $1/$5 per million tokens model, the 2,627 pages that fit before the uncompressed ceiling are:
input: 2,627 × 1,800 = 4,728,600 tok = 4.73 MTok × $1 = $4.73
output: 2,627 × 400 = 1,050,800 tok = 1.05 MTok × $5 = $5.25
-----
$9.98
Ten dollars. Nobody is going to notice ten dollars, and that is precisely the problem: the retry is automatic, the retry costs another $9.98, and the retry fails at page 2,627 again, because a disk ceiling is deterministic. This is the distinction between retry and resume showing up in a new place. A retry is the right response to a transient fault. ENOSPC on a fixed-size volume is not transient — it is the same input meeting the same constraint, and a retry loop around it burns the bill linearly while making no progress whatsoever.
The guard is about six lines
The fix is not clever. Check before you write, rather than discovering the answer from the write:
import { statfsSync } from "node:fs"
function freeBytes(path: string): number {
const s = statfsSync(path)
return Number(s.bavail) * Number(s.bsize)
}
class DiskBudget {
readonly path: string
readonly reserve: number
constructor(path: string, reserve: number) {
this.path = path
this.reserve = reserve
}
/** True if one more item of `bytes` still leaves the reserve intact. */
admits(bytes: number): boolean {
return freeBytes(this.path) - bytes >= this.reserve
}
}
And at the top of the loop:
const budget = new DiskBudget("/", 16 * 1024 * 1024) // logs, temp, the checkpoint itself
for (let i = 0; i < pages.length; i++) {
if (!budget.admits(estimatedPageBytes)) {
await checkpoint({ nextPage: i, reason: "disk" })
return // a stop, not a throw
}
await rasterise(pages[i])
await callModel(pages[i])
}
That is the entire difference between the two lines of output above. The reserve matters more than it looks: run to genuinely zero free bytes and you cannot write the checkpoint that makes the stop recoverable, cannot flush the log that tells you why, and in a container often cannot get a clean shutdown at all. Leave room for the machinery of failing well.
The estimate does not need to be good. It needs to be an upper bound, and it needs to be measured from your own documents rather than guessed — which is the same discipline as estimating tokens per page before quoting a job, and fails in the same way when skipped.
The three real fixes, in order of how much they buy
Do not keep the intermediate. The page image exists to be turned into tokens. Rasterise one page, send it, discard it, move on. A pipeline holding one page at a time has no disk ceiling at all — it has a concurrency ceiling instead, which is a far nicer thing to own because it is a number you chose.
Pick the format on evidence. The table at the top spans 60× between the worst and best option for these documents, for no quality difference that any vision model will notice at 300 DPI. Measuring it takes ten minutes.
Stream results out as they are produced. Not at the end. Every page written to object storage as soon as it is done is a page that survives the microVM, which turns a catastrophic failure into a resumable one and makes the disk question mostly moot. Vercel's own answer here is Drives — persistent volumes that mount into a sandbox — and every other sandbox provider has an equivalent. Any of them beats an ephemeral disk you are quietly betting the run on.
None of this is specific to Vercel. It is true of Fly Machines, of Cloud Run
jobs, of a Lambda's 512 MB /tmp, of any ECS task with a fixed ephemeral
volume. The provider changed a number this week and the number is better. The
structure underneath it did not change at all.
This is the sort of thing a
reliability audit finds in an afternoon, and
the sort of thing an agent runtime has to handle
before it is allowed near a queue. More in
retry is not resume,
cost ceilings for agent runs, and
tokens per page.
Storage change and resource limits from Vercel's published changelog and pricing
docs, checked 11 September 2026. Page sizes measured locally with pdftoppm at
300 DPI on the same six-page A4 document.
