Design a serverless functions platform
Design a platform like AWS Lambda or Google Cloud Run functions: developers upload a function, and it runs whenever an HTTP request or an event arrives, from zero copies to thousands in seconds, billed by the millisecond. The platform runs code from strangers side by side on shared machines, so it must start sandboxes fast, keep tenants strictly apart, and pack them tightly enough to make money.
1. The prompt
"Design a serverless compute platform: customers deploy functions that run on requests or events, scale automatically, and pay only while their code runs."
What makes it hard:
- Cold starts. Scaling to zero means the first request after a quiet spell must wait for a sandbox to be created, code downloaded and the runtime started.
- Untrusted neighbours. Thousands of customers' code runs on the same hardware; a bug or an attacker in one must never reach another.
- Bursty, tiny units of work. Millions of functions, most idle most of the time, a few suddenly needing 1,000 copies.
2. Frame the problem
How much traffic?
1 million deployed functions, 2 million invocations a second at peak. Median run time 100 ms; maximum 15 minutes.
By Little's law, 2 M/s × 0.1 s ≈ 200,000 sandboxes busy at any moment, plus idle warm ones.
What can a function be?
Code for a supported runtime (Node.js, Python, Java, Go…), or a container image, with 128 MB to 10 GB of memory.
CPU is handed out in proportion to memory, so memory is the unit of packing.
How is it triggered?
Synchronously (HTTP, an API call that waits for the result) and asynchronously (events, queues, schedules, where the caller doesn't wait).
Async needs a durable queue, retries and a dead-letter destination.
Concurrency model?
One request at a time per sandbox. More requests at once means more sandboxes.
Simple for customers to reason about; it makes sandbox creation speed the core problem.
Isolation?
Assume hostile tenants. Separate accounts must never share a sandbox or a kernel.
That rules out plain containers as the only boundary.
3. What it must do
- Deploy a function version (code, runtime, memory, timeout, environment).
- Invoke it synchronously or from events, running each invocation in an isolated sandbox.
- Scale automatically from zero to the account's limit, and back to zero.
- Meter and report: logs, metrics, and billing per millisecond of run time.
Not now: long-running services (that's a container platform), GPUs, a function marketplace, cross-region failover of individual invocations.
4. How well it must do it
| Target | Why this number |
|---|---|
| Warm invocation overhead under 10 ms at p99 speed | Most requests should feel like calling an ordinary service. |
| Cold start under 500 ms at p99 for small functions, platform part only speed | The customer's own initialisation (loading a framework) comes on top; we can only control our part. |
| Isolation: no data or resource leaks between tenants security | One breach ends the business. |
| Async events: never lost; retried, then sent to a dead-letter destination data safety | Events are often the only record of something that happened. |
| Scale-up: +1,000 concurrent sandboxes for one function within about 10 s elasticity | Traffic spikes are why people choose serverless. |
5. Numbers that change the design
| Quantity | Working | → so we… |
|---|---|---|
| Busy sandboxes | 2 M/s × 0.1 s ≈ 200,000, average size 512 MB ≈ 100 TB of memory busy; with warm idle sandboxes roughly double | → so ~1,000 hosts with 256 GB each, packed by memory |
| Sandbox overhead | A container on a shared kernel costs little but isolates weakly; a full VM isolates well but takes seconds and hundreds of MB | → so lightweight microVMs: Firecracker reports under 125 ms boot and under 5 MiB of overhead per VM |
| Code to fetch on a cold start | A 50 MB package at ~1 GB/s from object storage ≈ 50 ms; a 2 GB container image ≈ 2 s | → so code is cached on hosts, and large images are loaded lazily in chunks |
| Stored code | 1 M functions × several versions × ~50 MB ≈ 100+ TB | → so packages live in object storage, content-addressed (shared layers stored once) |
Illustrative numbers. Firecracker figures are from its project page (checked October 2026).
6. The nouns
- Function version: immutable code + configuration. An alias (
prod) points at a version. - Sandbox: a microVM running one function version for one account; idle, busy, or being created.
- Host: a machine running hundreds of sandboxes, with a local agent and a code cache.
- Invocation: one run, synchronous or from an event.
- Account limits: maximum concurrency, burst rate, reserved concurrency per function.
function_version (account, name, version) → runtime, memory_mb, timeout_s, code_digest, env (encrypted) sandbox id → host, function_version, state (creating | idle | busy), idle_since (in the worker manager's memory) host id → zone, free_memory, cached code digests, healthy invocation id → function_version, sandbox, start, duration_ms, status (stream → logs, billing) event_queue per async function: durable queue, attempts, dead-letter destination
7. The contract
PUT /v1/functions/{name} {runtime, memory_mb, timeout_s, code (zip or image digest)} → version
POST /v1/functions/{name}/aliases/prod {version}
POST /v1/functions/{name}:invoke body → result (sync; 429 when over the concurrency limit)
POST /v1/functions/{name}:invoke-async body → 202 (queued; retried twice, then dead-letter)
Triggers: HTTP routes, queue and stream subscriptions, schedules
Inside the sandbox: the runtime fetches the next event from a local runtime API, runs the handler, posts the result
8. First working version
A front end authenticates calls and enforces limits. A worker manager knows, for each function version, which sandboxes exist and which are idle. A placement service chooses a host for a new sandbox. On each host, an agent creates microVMs and caches code.
8.1 Warm and cold invocations
8.2 An event-triggered function
9. Make it hold up
9.1 Making cold starts rare and short speed · cost
| Approach | Why | |
|---|---|---|
| ✗ Bad | Create a fresh sandbox for every invocation | Every request pays boot + code fetch + runtime start: hundreds of ms to seconds. |
| ~ Good | Keep every sandbox warm forever | No cold starts after the first, but memory is held for functions that ran once last week. The platform can't afford it, so customers would pay for idle time. |
| ✓ Great | Reuse idle sandboxes for a few minutes, and make creating one cheap: microVMs that boot fast, code cached on hosts (and lazily loaded image chunks), snapshots of an initialised runtime restored instead of booted, and paid provisioned concurrency for functions that can't tolerate any cold start | Busy functions almost never see a cold start; rarely used ones pay a short one; customers who need a guarantee pay for warm capacity explicitly. |
The customer controls the rest: a small package, few dependencies, and work done lazily rather than at start-up. The lab in section 13 shows how the keep-alive time trades cold starts against idle memory.
9.2 Strangers on one machine security
9.3 Packing hosts cost · reliability
- Memory is the unit. CPU share and network bandwidth are given in proportion to memory, so packing by memory also bounds the rest.
- Spread each function across many hosts and zones: a host failure then costs a function a few of its sandboxes, not all of them.
- Mix workloads. Functions from different customers peak at different times; packing a host with one customer's correlated functions would make it hot when that customer spikes.
- Prefer hosts with the code cached, so placement saves the fetch.
- Overcommit carefully: idle sandboxes can be squeezed (memory reclaimed), busy ones can't.
9.4 Limits and bursts fairness
Each account has a concurrency limit (say 1,000 sandboxes) and a burst rate (how fast new sandboxes may be added). Limits protect the platform from one customer's runaway loop, and protect the customer from a surprise bill. A function can have reserved concurrency: guaranteed capacity, and also a cap, which is how a customer stops a function from overwhelming their database. Over the limit, synchronous calls get 429; asynchronous events wait in their queue and are retried.
9.5 The database behind the functions reliability
A traffic spike scales the function to 1,000 sandboxes, each opening its own database connection: the platform scaled perfectly and the customer's database fell over. Recommend a connection proxy or pooler in front of the database, reserved concurrency as a cap, and short-lived connections or an HTTP data API. Functions are stateless; anything that must survive goes to a database, object storage or a queue, never to the sandbox's disk.
9.6 The whole design after the deep dives
10. Interviewer follow-ups
A host dies with 300 busy sandboxes.
Synchronous callers get an error and may retry; async invocations are still in their queue (deleted only on success), so they run again elsewhere. The worker manager drops the host's sandboxes and new ones are created on other hosts.
The worker manager is a single point of failure.
Partition it by function (consistent hashing), with replicas per partition. Its state (which sandboxes exist) can be rebuilt from host agents' reports, so losing a partition costs a burst of cold starts, not data.
A function runs for its full 15 minutes on every call because of a bug.
The timeout kills it and it's billed for 15 minutes; concurrency limits stop it multiplying. Alarms on duration and error rate tell the customer.
How do you deploy a new version of the platform's own host software?
Drain hosts in small waves: stop placing new sandboxes, let busy ones finish (up to the maximum timeout), then upgrade. Never upgrade a whole zone at once (Module 27).
How is billing computed?
The host agent records start and end of each invocation; duration × memory size (GB-seconds) plus a per-request fee, aggregated from the invocation stream, as in D17.
Snapshots restore the same memory into many sandboxes. Any danger?
Yes: anything random generated during initialisation (seeds, unique IDs, keys) is now identical in every copy. Runtimes must re-seed after restore, and the platform should offer a hook for it.
11. When things break
| Failure | What users see | How we detect it | What limits the damage |
|---|---|---|---|
| Host lost | Some in-flight calls fail | Agent heartbeats | Functions spread over hosts and zones; async retried |
| Code store slow | Long cold starts | Fetch latency | Host code caches; warm sandboxes unaffected |
| Placement service down | No new sandboxes; warm ones keep working | Placement errors | Replicated; worker manager queues briefly |
| One account bursts | That account throttled | Concurrency per account | Account limits and burst rates |
| Sandbox escape attempt | Nothing, if contained | Host security monitoring | MicroVM boundary, minimal host attack surface, no cross-tenant reuse |
| Event queue backlog | Async work delayed | Queue age | Pollers scale out; events kept until done |
12. Wrap-up and the clock
Trade-offs we made:
- MicroVMs for isolation, accepting more complexity than containers.
- Reuse idle sandboxes for minutes, trading memory for fewer cold starts; provisioned concurrency as a paid guarantee.
- One request per sandbox: simple for customers, more sandboxes for us.
- Pack hosts by memory, spread each function widely.
- Limits per account and per function, to protect the platform and the customer's own systems.
What we'd watch: cold start rate and duration (platform part) · warm overhead · host memory utilisation · sandbox creation rate per host · throttles per account · async queue age and dead-letter counts.
At 10×: more hosts and worker manager partitions. The cost lever is utilisation: better snapshots and faster creation let idle sandboxes be reclaimed sooner.
A 60-minute interview
| 0–5 | Frame: traffic, durations, triggers, isolation |
| 5–10 | Must do, how well, Little's law |
| 10–15 | Nouns and contract |
| 15–25 | First version: warm and cold paths |
| 25–50 | Deep dives: cold starts (9.1), isolation (9.2), limits (9.4) |
| 50–60 | Packing or async, follow-ups, wrap-up |
A 90-minute interview
| 0–8 | Frame, including the concurrency model |
| 8–15 | Must do, how well, numbers |
| 15–22 | Nouns, contract |
| 22–35 | First working version, sync and async |
| 35–75 | Deep dives 9.1–9.5 and the final picture |
| 75–85 | Follow-ups: host loss, snapshots, billing |
| 85–90 | Trade-offs, metrics, 10× |
| Level | What a strong answer shows |
|---|---|
| Mid-level | Containers started on demand, a router that reuses running ones, autoscaling, a queue for events. |
| Senior | Warm and cold paths, Little's law for capacity, microVM isolation, code caching and snapshots, limits and reserved concurrency, async retries and dead letters. |
| Staff+ | Packing economics and keep-alive as a cost trade-off, snapshot uniqueness pitfalls, failure domains for the control plane, protecting customers' downstream systems. |
Common mistakes:
- Plain containers on a shared kernel for untrusted tenants, without saying why that's acceptable.
- Ignoring cold starts, or "keep everything warm".
- Forgetting that concurrency = rate × duration.
- No limits, so one customer's loop takes the fleet.
- Losing async events on a host failure.
13. Try it, check yourself, read more
The keep-alive lab. A function gets requests at random. Choose how often, how long each runs and how long the platform keeps an idle sandbox, and see the cold start rate and the memory held idle.
A simple model: requests arrive at random (Poisson); a request is cold if no sandbox has been used within the keep-alive time. At high rates, cold starts come from scaling up, which this model ignores. 512 MB sandboxes.
A function gets 20,000 requests a second and each takes 100 ms. Roughly how many sandboxes are busy at once?
Which isolation fits untrusted code from many customers on one host?
What cuts the runtime part of a cold start the most for a Java function?
A spike scales a function to 1,000 copies and the customer's database runs out of connections. The fix?
| Resource | Why |
|---|---|
| Firecracker: lightweight virtualization for serverless applications (NSDI 2020) | Why AWS built microVMs for Lambda, and what they cost. |
| Firecracker project page | Boot time, overhead and the security model. |
| Lambda provisioned concurrency | A product's version of paid warm capacity. |
| gVisor | A different isolation approach: a user-space kernel in front of the host. |