The Carbon and Energy Case for Scale-to-Zero Compute
There is a version of this post that is greenwash, and I am not going to write it. It goes: our platform is efficient, therefore using it is good for the planet. That claim is unfalsifiable as stated, and I have no emissions data to back it with. The version worth writing is narrower. Idle compute has a physical cost that most engineers assume away; scale-to-zero is one of the few architectural levers that touches that cost directly; and the lever only works under a condition that a lot of platforms quietly fail to meet. The condition is the interesting part.
A server at zero load does not draw zero power
The mental model almost everyone carries is that a machine doing nothing costs nothing to run, and that power tracks utilisation the way a taxi meter tracks distance. It does not. This is the energy-proportionality problem, named and measured by Barroso and Holzle at Google in the late 2000s, and revisited in the literature many times since: the power-versus-utilisation curve for a server does not pass through the origin. It starts high and rises gently.
I am deliberately not going to give you a precise figure, because a precise figure would be invented. The general industry range that gets quoted is that an idle server draws somewhere in the region of a third to a half of its peak power, and every part of that depends on the machine: CPU generation and how aggressively it enters package C-states, how much DRAM is populated and refreshing, PSU efficiency at low load, fan curves, whether the BMC and NICs let anything sleep at all. Modern platforms are better than the ones Barroso measured. They are not proportional. Treat any single number you see, including that range, as directional rather than authoritative, and measure your own hardware if the answer matters commercially.
Two consequences follow, and they are the only physics this post needs. First, the marginal energy cost of a machine that exists but is not working is large, not small. Second, whatever the server draws gets multiplied by the facility overhead before it reaches the meter: cooling, distribution losses, the rest of the PUE tax. Idle watts are not cheap watts. They are ordinary watts spent on nothing.
A fleet of warm-but-idle VMs is close to the worst possible shape
Now overlay that on the standard way our industry hides cold starts. A warm pool is an inventory of machines and guests deliberately held in the state that maximises electricity consumed per unit of useful work: powered on, memory populated, kernel scheduled, network attached, health-checked on a timer, and doing absolutely nothing until a request arrives. You are paying close to the power of real work in exchange for none of the work.
That is not a criticism of the people who build warm pools. They are solving a real problem, which is that a user will not wait eight seconds for a container image pull and a language runtime to boot. The pool is a latency instrument, and it works. It is just worth being clear-eyed that its running cost is not an accounting abstraction. It is a physical draw on a physical grid, whether it appears on your invoice or somebody else's.
The same shape shows up one layer down as memory. A guest that is merely alive holds real resident RAM even when it is running nothing, and DRAM does not stop refreshing because the workload went quiet. Idle memory is the least visible and most persistent form of this waste, because unlike CPU it never shows up as a spike on anybody's dashboard.
The caveat that makes the argument honest: freed is not saved
Here is where most sustainability claims about serverless quietly break, so I want to state it before making any positive claim of my own. Turning your workload off does not, by itself, save any energy. Your code did not become more efficient; it is the same code. The entire environmental claim rests on what happens to the capacity you released, and there are exactly three outcomes.
- Another tenant's work lands on the host you vacated. This is a genuine saving, because that work would otherwise have needed capacity that did not previously exist. The saving is real but it belongs to the fleet, not to you.
- The fleet shrinks. Fewer machines run, or machines are actually powered down. This is the largest saving available and the rarest, because most fleets are sized for peak and never contract.
- The provider immediately refills the space with idle warm capacity held on your behalf. Nothing is saved at all. The electricity is still burning; it has simply moved from your line item to theirs, and you now feel virtuous about it.
This is the point at which I have to declare an interest, because it is also the architectural bet PandaStack made for entirely unrelated reasons. There is no warm pool. Every create restores a baked Firecracker snapshot on demand, at a p50 of around 179ms, which means there is no inventory of idling guests to hide latency behind. That decision was made on cost and capacity-planning grounds years before I thought about it in energy terms, and it happens to be the property that makes the third outcome above impossible by construction. I would rather present it as a lucky alignment than as foresight.
What "off" actually means in the implementation
"Scale-to-zero" is used loosely enough to be nearly meaningless, so it is worth saying precisely what happens, because the difference between the loose and strict versions is exactly the difference between saving energy and not. Several platforms describe a workload as scaled to zero when the process is stopped but the guest is still resident, the memory still mapped, the machine still allocated. That releases very little.
In our apps path the idle sweep runs on the 30-second monitor tick and, for a running app with auto-hibernate enabled whose last request is older than its idle timeout, deletes the sandbox outright. The comment in the source is blunter than any marketing copy would be: CPU, RAM and disk are all freed, and the app row is left with no sandbox at all. What survives is an immutable disk image baked at deploy time in object storage, plus a memory seed captured lazily on the way to sleep. A later request cold-boots a fresh sandbox from that image on any host in the fleet, with no lease pinning to the machine it used to live on.
apps: app hibernated (sandbox deleted; CPU+RAM+disk freed, GCS image retained)
The no-pinning detail is the one that matters environmentally. If a sleeping app reserved its old host, the host could not shrink and the capacity was never really returned. Because the wake can land anywhere, a host whose apps have all gone to sleep is a host with nothing on it, which is a host the scheduler can pack from scratch or, in a fleet that autoscales down, retire.
The default idle timeout is 900 seconds, configurable per app down to a floor of 60. Fifteen minutes is a compromise between wake frequency and dwell time, not a physically derived number.
# Tighten an app's idle window so it sleeps sooner.
curl -X PATCH "$PANDASTACK_API/v1/apps/$APP_ID" \
-H "Authorization: Bearer $PANDASTACK_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"auto_hibernate": true, "idle_timeout_seconds": 180}'The thing that silently defeats all of this: your uptime monitor
This one caught us in production and it is worth flagging because it defeats scale-to-zero on every platform that implements it naively. Put an app on a public URL and it acquires a steady drizzle of automated traffic that the obscure default URL never saw: uptime monitors on a five-minute interval, health probes, SEO crawlers, security scanners. Every one of those requests looks like a request. Each resets the idle timer, and each one arriving at a sleeping app cold-boots it. A single free-tier uptime monitor is therefore sufficient to keep a workload running permanently, burning power around the clock so that a dashboard somewhere can stay green.
The fix is a traffic classifier on the host router that answers two separate questions about every inbound request, deliberately with different false-positive tolerances.
- Does this keep the app warm, meaning should it reset the idle timer? This set is broad, because a false positive only makes the app sleep slightly sooner, which is safe. HEAD and OPTIONS, favicon and robots.txt, conventional health paths, an empty user-agent, named monitors, and every crawler all fail to keep an app warm.
- Does this justify a wake, meaning should a sleeping app cold-boot to answer it? This set is deliberately narrow, because a false positive withholds the real app from a real client. Only named uptime monitors, HEAD, and pure asset paths are refused a wake; they get a cheap 200 with an X-Pandastack-App: asleep header instead. Crawlers do wake the app, because Googlebot must see real content, not a placeholder.
- Everything else is treated as a real user: it warms, and it wakes.
The list of monitor user-agents is a hardcoded set of vendor tokens, which is exactly as unsatisfying as it sounds and needs occasional maintenance. It is the pragmatic answer. There is no protocol-level way to declare "I am a probe, do not wake anything on my behalf", which is a genuine gap in how the web talks to sleeping infrastructure.
Sub-second restore is what makes turning things off socially acceptable
The behavioural argument matters more than the mechanism. Nobody turns anything off if turning it back on is painful. This is true of office lights and it is true of infrastructure: if a wake costs forty seconds, the first time a colleague clicks a staging link and stares at a spinner, somebody disables auto-hibernate for the whole workspace and it never gets turned back on. The energy saving is then precisely zero, and you have a feature flag nobody trusts.
So restore latency is not a performance nicety here, it is the enabling condition. Our own history on this is instructive and slightly embarrassing. When we first measured a real wake end to end, the VM was restored and resumed in about 1.4 seconds and the user waited roughly fifteen. The other thirteen seconds were our own control-plane ceremony after the restore: polling sandbox status on a timer, probing SSH, rewriting an environment file the already-running process could not observe, and a disk probe whose global sync flushed the page cache we had just faulted in. The physics was never the problem. Our sequencing was. Moving all of it off the critical path, and gating traffic on the single fact that the app answers HTTP, took the user-visible wake to a bit over a second.
The larger argument is embodied carbon, and it is an argument for density
Operational power is the part everyone reaches for. The manufacturing side is often the larger term and almost never discussed. A meaningful share of a server's lifecycle emissions is embodied in making it: fabricating the silicon, the DRAM, the drives, the board, the chassis, and shipping the result. Published lifecycle assessments vary enormously and I would not quote a percentage at you, but there is a nuance that follows from the arithmetic regardless of the exact split. The cleaner your electricity, the smaller the operational term becomes, and therefore the larger the embodied term looms in proportion. On a low-carbon grid, the most useful thing an infrastructure team can do is cause fewer servers to be manufactured.
Which turns a sustainability question into a density question, and density is a question about accounting. We learned this the hard way. Our original host memory admission gate summed the baked memory of every live sandbox and refused a create when the total would exceed the host budget. Reasonable-sounding, and catastrophically conservative: every first-party template bakes 4 GiB, Firecracker faults pages in lazily, and a guest that is merely alive holds a few hundred megabytes of real RAM. On 3 September 2026 the fleet started refusing every create and wake with "no compute capacity" while holding 14 booked VMs that between them were using 2.4 GiB of 62 GiB of physical memory.
Read that as an emissions story rather than an outage story and it is worse. We were minutes away from adding hardware in order to serve memory that nobody was touching, because our bookkeeping described a machine that was 96 percent empty as full. The replacement gate charges the measured resident set of the squeezable classes plus a reserve for creates still in flight. Twelve days of shadow measurement across 27,200 samples put the ratio of resident to committed memory at a median of 0.06 to 0.08 and a p95 of 0.20 to 0.27, which is the empirical version of the same statement: committed accounting was overstating real demand by more than an order of magnitude at the median.
Managed databases are still charged their full committed memory and never overcommitted, which is a deliberate exception. A database that gets squeezed is a database that loses data, and no density argument is worth that. Honest limitation: the density gains here apply to the squeezable classes only.
Memory streaming and zero elision are density levers, therefore energy levers
The same logic applies further down. When a snapshot is restored, the guest's memory is served on demand through a userfaultfd handler rather than materialised up front, with 4 MiB chunks fetched from object storage only when the guest actually touches them. On our seeds roughly 85 percent of restore faults turn out to be zero pages, which is unsurprising once you say it out loud: a 4 GiB guest that has just booted has not written most of its address space.
Serving those faults by copying a zeroed buffer allocates a fresh private page per fault per VM, so most of a guest's faulted-in memory becomes real resident RAM that co-resident guests cannot share. Serving them by mapping the shared kernel zero page copy-on-write costs approximately no resident memory and only materialises a private page if the guest ever writes it. That is a kernel-API detail with a straight line to hardware: more guests per host, fewer hosts, fewer machines manufactured. The lever people build for cost and the lever people build for sustainability turn out to be the same lever, which is the most encouraging thing in this post.
There is a real trade being made. A sleeping app's bytes live in object storage rather than on a powered host's disk, and object storage is not free either. Cold bytes at rest have a very different power profile from DRAM, but they are not zero, and every sleeping workload is a durable object somebody's drives are spinning for. I do not have the data to quantify the net, and I am not going to pretend otherwise.
What I am not claiming
This is the section that decides whether the rest of the post was honest, so it is longer than it is fun.
- We publish no emissions figures, and there is no carbon dashboard. Getting trustworthy per-sandbox energy numbers means host-level power telemetry attributed to guests by a model, and every such model I have looked at is an estimate wearing a decimal point. Publishing one would be inventing precision.
- No certification, no offset programme, no green badge. If a platform's sustainability page leads with a logo rather than an architecture, the logo is the product.
- The rebound effect is real and cuts against everything above. Making always-available compute cheap causes more of it to exist. Scale-to-zero lowers the price of leaving a thing deployed indefinitely, so people leave more things deployed indefinitely. At the level of one workload the saving is genuine; at industry level the net is not obviously negative.
- Region and grid carbon intensity dominate. Choosing a region with clean electricity is a larger single lever than any architectural decision in this post, and it takes an afternoon rather than a rewrite.
- The workload matters more than the substrate. If your app spins a core in a polling loop, none of this helps you. Scale-to-zero addresses the time your code is not running. It has nothing to say about the time it is.
What is left after all those subtractions is still worth something, and it is this: the shape of a fleet, specifically how much of it is powered but not working, is an engineering decision, and it is one of the few decisions where the efficient answer and the cheap answer point the same way.
The questions worth asking a platform
If you want to evaluate this for yourself rather than take a vendor's word, including mine, these are the questions that separate an architecture from a claim.
- When my workload scales to zero, is the guest deleted or merely stopped? If the memory is still mapped and the machine still allocated, nothing was returned.
- Do you keep a warm pool to hide cold starts? If yes, the capacity I release is refilled with idle capacity and the fleet-level draw does not change.
- Is a woken workload pinned to the host it slept on? Pinning means the host cannot be emptied, which means it cannot be retired.
- How is host capacity accounted for: committed allocation or measured use? Committed accounting buys hardware to serve memory nobody is touching.
- What does an uptime monitor do to a sleeping workload? If the answer is "wakes it", scale-to-zero is off for any app with a public URL and a status page.
- How long is a wake? Not the p50 of the restore, the p50 of the first byte the user receives. If it is tens of seconds, the feature will be disabled within a fortnight and none of the rest matters.
That last one is the whole post in miniature. The environmental case for scale-to-zero is not made by the part that turns things off. It is made by the part that turns them back on fast enough that nobody objects.
Frequently asked questions
Does scale-to-zero actually reduce carbon emissions, or just my bill?
It reduces your bill unconditionally and reduces emissions conditionally. The condition is that the capacity you release is genuinely reclaimed rather than refilled with idle capacity held on your behalf. If the platform runs a warm pool to hide cold starts, the machines stay powered and the electricity keeps burning, just on the provider's meter instead of yours. If the platform has no warm pool, the freed host either takes another tenant's work, which avoids provisioning new capacity elsewhere, or sits genuinely empty and can be packed or retired. So the honest answer is that this is a property of your provider's architecture rather than of your application, and the diagnostic question is whether the guest is deleted or merely stopped when it sleeps.
Isn't a microVM heavier than a container, and therefore worse for energy?
Per instance at rest, a microVM carries more overhead than a container, though far less than a classic VM: Firecracker's device model is deliberately tiny and the per-guest memory overhead is on the order of a few megabytes rather than hundreds. The comparison that matters is not per-instance overhead, though, it is instances per host and hosts per fleet. A microVM platform that faults guest memory in lazily, maps zero pages copy-on-write, shares disk through reflink clones, and admits on measured resident memory rather than committed allocation can pack more useful work onto a machine than a container platform that conservatively reserves what each workload declared. Measure the density of the whole host under your real workload mix rather than reasoning from the overhead of one guest.
Doesn't waking a VM repeatedly burn more energy than just leaving it running?
There is a real break-even and it depends on your duty cycle. A wake does concrete work: fetching memory chunks from object storage, faulting them in, resuming the guest, running a health check. Against that, leaving a guest resident holds memory and a scheduling slot continuously, and idle draw is a large fraction of peak rather than a small one. The relevant comparison is the energy of one wake against the idle energy of the interval you avoided, which is why the default idle timeout is 900 seconds rather than 30: a very short timeout produces wake thrash for a workload receiving sporadic traffic. If your app is touched every couple of minutes all day, leave it running and set auto_hibernate to false. If it is touched a few times an hour, or a few times a day, the sleep is a straightforward win. Sleeping is for workloads with genuine gaps, not for shaving seconds off a busy one.
What about the energy cost of the snapshot and image storage you keep while an app sleeps?
It is real, and I have not quantified it. A sleeping app on our platform is an immutable disk image plus a memory seed in object storage, so the bytes are durable somewhere and durable bytes need powered drives, replication and a control plane. The intuition that this is a good trade rests on cold storage having a very different power profile per byte from DRAM in a running host, plus the fact that the image exists anyway as the deploy artefact whether the app is sleeping or not. But the intuition is not a measurement, and anyone claiming a precise net figure for this without access to their storage provider's power data is guessing. Treat storage as the cost side of the ledger and be suspicious of anyone who forgets to mention it.
Can I get per-sandbox energy or carbon numbers out of PandaStack?
No, and I would rather say so plainly than ship an estimate with a decimal point on it. Producing credible per-guest energy figures requires host-level power telemetry, typically from RAPL or the BMC, attributed down to individual guests by a model that apportions a shared machine's draw across tenants. Every such model involves assumptions about what an idle baseline is and how to divide it, and the resulting number is far softer than its presentation usually implies. What you can get is the accounting that actually drives both fleet size and your invoice: CPU-seconds actually burned, and memory held over time. If you need auditable emissions numbers for reporting, get them from the underlying cloud provider's own disclosures for the regions your workloads run in, not from us.
Keep reading
- Snapshot-restore vs warm pools — The architectural decision this whole argument depends on, argued on cost and latency.
- Always-on vs scale-to-zero agent infra — The same trade-off for bursty agent workloads, with the economics rather than the watts.
- The economics of microVM density — Why packing more tenants per host is the lever, and what actually limits it.
- MicroVM memory oversubscription — How committed accounting overstates real demand, and what to charge instead.
- The bot-traffic wake classifier — The full detail of stopping an uptime monitor from keeping a sleeping app awake.
- Apps on PandaStack — Where auto-hibernate and the idle timeout live if you want to try the behaviour.
49ms p50 cold start. Fork, snapshot, and scale to zero.