PracHub
QuestionsLearningGuidesInterview Prep
|Home/System Design/OpenAI

Design a sandboxed cloud IDE

Last updated: Apr 9, 2026

Quick Overview

This question evaluates a candidate's competency in designing multi-tenant distributed systems with strong isolation and sandboxing, runtime lifecycle management, real-time output/log streaming, data persistence, autoscaling, and observability.

  • easy
  • OpenAI
  • System Design
  • Software Engineer

Design a sandboxed cloud IDE

Company: OpenAI

Role: Software Engineer

Category: System Design

Difficulty: easy

Interview Round: Onsite

## System design: Sandboxed cloud IDE (Colab-like) Design a **multi-tenant, browser-based cloud IDE/notebook** that lets users run code in an isolated sandbox (similar to hosted notebooks). ### Core user experience - User opens a workspace (project/notebook), edits code in the browser, and runs cells/commands. - Output appears in the UI (stdout/stderr, rich output). - Users can view **streaming logs** while code runs. ### Requirements **Functional** - Provision an isolated compute environment per workspace/session. - Execute arbitrary user code safely (sandboxing). - Stream execution output/logs to the browser in near real time. - Support basic file operations (upload/download, persisted workspace state). - Basic collaboration is optional (call out if you include it). **Non-functional** - Strong isolation between tenants (security is primary). - Reasonable startup latency for a new session. - Support autoscaling and fair resource sharing. - Observability: metrics, tracing, audit logs. ### Focus areas to cover - How you choose and manage the compute substrate (VMs vs containers vs microVMs). - Isolation model (filesystem, network, process, credentials). - Log/output streaming architecture. - Lifecycle management: create, run, idle, suspend/resume, terminate. - Data persistence strategy (workspace files, checkpoints). State assumptions and provide an API sketch and high-level architecture diagram description.

Quick Answer: This question evaluates a candidate's competency in designing multi-tenant distributed systems with strong isolation and sandboxing, runtime lifecycle management, real-time output/log streaming, data persistence, autoscaling, and observability.

Solution

### 1) Clarify scope, assumptions, and rough scale I'll design a **Colab-like, multi-tenant cloud IDE** where the primary constraint is **safely running untrusted code**. Everything else (latency, density, cost) is optimized *subject to* that isolation guarantee. **In scope:** per-session isolated compute, secure execution of arbitrary code, near-real-time output streaming, durable workspace files, full session lifecycle. **Optional:** real-time collaboration (called out at the end). **Assumptions:** - Workloads are bursty and interactive — short cells, long idle gaps. Mostly Python/JS; a minority need a GPU (which, as §3 explains, forces a *different* substrate — GPUs can't ride the default microVM). - A session is single-user; multiple sessions belong to a workspace. - Untrusted by default: assume the user is an attacker trying to escape the sandbox, exfiltrate data, mine crypto, or reach internal services. - We control a cloud fleet (bare-metal or nested-virt-capable hosts) and can run a microVM layer; GPU hosts are provisioned separately. **Back-of-envelope (to ground density/lifecycle decisions):** - Target ~100k registered users, ~5k concurrent *active* sessions at peak, plus a long tail of *idle/suspended* sessions (say 10×, ~50k). - A modest session ≈ 1–2 vCPU, 2–4 GB RAM. So peak active ≈ 5–10k vCPU and 10–20 TB RAM. Sizing the fleet from both dimensions: at ~64–128 vCPU/host the vCPU side needs ~80–150 hosts; at ~256–512 GB RAM/host the memory side needs ~25–60 hosts. The vCPU side dominates, so **~80–150 CPU hosts** at peak (plus headroom for fragmentation and warm pools). GPU capacity is sized separately off GPU demand. - The idle/suspended tail must cost near-zero (no running CPU/RAM), which drives the **suspend-to-snapshot** strategy below. - Cold-start target: a fresh session should be interactive in **single-digit seconds** (p95), which rules out booting a full guest OS on the request path and motivates **warm pools + snapshot restore**. --- ### 2) High-level architecture Two planes: a multi-tenant **control plane** and a per-session **data plane**. The browser never talks directly to a runtime. ``` Browser (Monaco editor + notebook UI + xterm) │ HTTPS (control) │ WSS (output stream) ▼ ▼ ┌─────────────── Control Plane ───────────────┐ ┌──── Streaming Gateway ────┐ │ API Gateway / AuthN-AuthZ │ │ sharded WS terminators, │ │ Workspace Service (metadata, files index) │ │ auth, fan-out, replay, │ │ Session Manager (lifecycle, quotas) │ │ backpressure │ │ Scheduler/Capacity (bin-pack, autoscale) │ └────────────┬──────────────┘ │ Policy Engine (images, egress, limits) │ │ gRPC └──────────────┬───────────────────────────────┘ │ │ provision / control (gRPC) │ ▼ ▼ ┌──── CPU Host (bare metal) ────┐ per-session microVM │ Host Agent ── Firecracker/ │ ───► ┌──────────────────────────┐ │ jailer; warm microVM pool │ │ Guest kernel (locked down)│ └───────────────────────────────┘ │ Runtime Agent (exec, fs, │ │ log capture, health) │ ┌──── GPU Host (single-tenant) ─┐ │ user kernel (Jupyter-like)│ │ Host Agent ── full VM w/ PCIe │ └──────────────────────────┘ │ passthrough OR hardened │ │ NVIDIA-stack container │ └───────────────────────────────┘ Object store (workspace files, snapshots, large rich-output payloads) Log store (durable output + audit) ``` - **Control plane** services are stateless (any instance serves any request), backed by a metadata DB (Postgres) and a queue for async provisioning. - **Data plane** is two host fleets: **CPU hosts** running a warm pool of microVMs, and **GPU hosts** running the GPU substrate (see §3). Each host runs a **Host Agent** that manages its runtimes and launches/snapshots them. - **Streaming Gateway** is a *separate, stateful* tier (it holds per-session replay buffers and routing) — see §8 for how it's sharded. - **Runtime Agent** runs *inside* each runtime as the only privileged-ish process the platform controls; it execs user code, captures output, serves file ops, and reports health. The same agent runs in both the CPU and GPU substrates. --- ### 3) Compute substrate: VM vs container vs gVisor vs microVM The core decision. For **untrusted** code, a shared host kernel is the attack surface — a single kernel CVE means a full host compromise across tenants. That pushes toward kernel-level isolation. | Option | Isolation | Cold start | Density | Notes | |---|---|---|---|---| | Plain container (namespaces + cgroups) | Weak — shared host kernel, huge syscall surface | ~ms | Highest | Unacceptable alone for untrusted code; one kernel escape = host-wide breach | | Hardened container (seccomp + AppArmor + dropped caps + read-only rootfs) | Better, still shared kernel | ~ms | High | Reduces surface but doesn't eliminate the shared-kernel risk | | **gVisor** (userspace kernel intercepts syscalls) | Strong-ish — sandboxed kernel, no real VM | tens to hundreds of ms | High | Great middle ground; some syscall-compat gaps and CPU overhead | | **microVM** (Firecracker / Cloud Hypervisor) | Strong — hardware-virtualized, own guest kernel, tiny VMM device surface | ~100 ms boot, faster via snapshot | High (low per-VM overhead) | **Default choice for CPU-only untrusted execution** | | Full VM (QEMU/cloud image) | Strongest, but heavy device model | seconds | Low | Heavy/slow for the common case, **but the only path that supports GPU PCIe passthrough** | **Default (CPU) substrate: microVMs.** Firecracker gives near-VM isolation with a minimal device model (virtio-net, virtio-block, a serial console — no BIOS, no PCI), ~5 MB of memory overhead per VM, and ~125 ms boot. A common production pattern is **Kata Containers using Firecracker (or Cloud Hypervisor) as the VMM** so each "pod" is actually a microVM while keeping a Kubernetes-native control surface — a reasonable way to manage the fleet. gVisor is a good *secondary* tier for cheaper/short-lived "scratch" runs where the perf overhead is acceptable. **GPU substrate (explicit, separate path).** Firecracker's minimal device model is exactly why it *cannot* host a GPU: it has **no PCI bus and no PCIe/VFIO passthrough**, so a GPU device cannot be attached to a Firecracker microVM. GPU sessions therefore run on a **different substrate**, on a separate pool of **single-tenant GPU hosts**: - **Preferred:** a **full VM (QEMU/Cloud Hypervisor) with VFIO PCIe passthrough** of a dedicated GPU (or a MIG slice). This keeps the per-tenant-kernel isolation property while exposing the device; it pays a heavier boot/device-model cost, mitigated with a small warm pool of pre-attached GPU VMs. - **Alternative** where passthrough VMs aren't available: a **hardened container with the NVIDIA container stack** (seccomp + AppArmor + dropped caps + read-only rootfs) on a host that is **never co-tenanted** with another customer for the GPU's lifetime — accepting the weaker (shared-kernel) isolation explicitly, and confining the blast radius by single-tenanting the whole host. - Either way, GPU hosts are **physically separate from the CPU microVM fleet**, scheduled from their own pool (§11), and a GPU session never shares a host with another tenant. So the rule is: **microVM by default; GPU sessions are routed to the GPU pool and run as passthrough full-VMs (or single-tenant hardened containers).** The substrate is chosen per session from the requested `gpu` count. Operationally (CPU fleet): - Run Firecracker under **`jailer`** (chroot, dedicated uid/gid, cgroup, separate network namespace) so even a VMM compromise is contained on the host. - Hosts are bare-metal (or nested-virt enabled) since Firecracker needs KVM. - One runtime per session, never shared across tenants — on both fleets. --- ### 4) Isolation model (filesystem, process, network, credentials) This is the security core; isolation is layered so no single bypass is fatal. The controls below apply to both substrates; the GPU substrate additionally relies on single-tenant host placement to compensate for its weaker (when container-based) kernel isolation. **Process / kernel:** each session = its own guest kernel (microVM, or passthrough full VM on GPU). Inside, the user process runs unprivileged with `seccomp` (deny dangerous syscalls), dropped Linux capabilities, and cgroups for CPU/mem/PID/IO limits. The host never trusts the guest. **Filesystem:** - Guest rootfs is a **read-only base image** (language toolchain + agent) plus a **writable overlay** for the user's workspace and installed packages. - Disk quota on the overlay (per-tier cap) to stop a user filling the host. - No host paths are bind-mounted into the guest beyond the controlled block device. **Network (most-attacked path):** - Each runtime gets its own **network namespace + veth pair + NAT**; no L2 adjacency between tenants. - **Default-deny egress.** Outbound goes through an **egress proxy** that allowlists package repos (PyPI, npm, conda, apt mirrors). Everything else is dropped. - **Block the cloud metadata endpoint** (`169.254.169.254` and the link-local range) at the host firewall — this is the classic SSRF→credential-theft path on cloud providers. As defense-in-depth, **also enforce IMDSv2** (token-required, session-oriented, with a hop limit of 1) on every host so that even if the firewall block regressed, a guest can't trivially reach the metadata service via SSRF. - No inbound from the internet; the runtime is reachable only by the control plane and streaming gateway over an internal network. **Credentials / identity:** - The guest holds **no node-level or cloud credentials**. The Host Agent never injects long-lived secrets. - If a session needs to reach an internal resource (e.g., the user's object-store prefix), it uses a **short-lived, narrowly-scoped token** minted per session, audited, and expiring on idle/terminate. - Snapshots restored from a warm pool must **re-seed entropy and per-session identity** so two sessions don't start from identical RNG state or share a token. **Supply chain:** base images are signed and scanned; package installs flow through the proxy where they can be scanned/cached; abusive runtime behavior (sustained max CPU/GPU, known crypto-miner signatures, mass-egress attempts) is detected and the session throttled or killed. --- ### 5) Session lifecycle States and transitions: ``` Provisioning ──► Running ──► Idle ──► Suspended ──► Terminated ▲ │ │ └───────────┴──────────┘ (resume) ``` - **Provisioning:** Session Manager admits the request (quota check), Scheduler picks a host from the **right pool** (CPU vs GPU, per the requested `gpu` count), Host Agent **claims a warm runtime** and attaches the workspace overlay. Because the runtime is pre-booted (or snapshot-restored), this is sub-second to a few seconds rather than a full boot. (GPU passthrough VMs warm-pool more coarsely since they're heavier and scarcer.) - **Running:** Runtime Agent serves exec/fs; output streams out. - **Idle:** no active client WS *and* low CPU for N minutes → mark Idle (still resident, but a candidate for suspend). - **Suspend (key cost lever, CPU substrate):** snapshot the microVM (memory + disk state) to the host/object store and **free the host's CPU and RAM**. This is what makes the 50k-idle tail cheap. Firecracker's **snapshot/restore** persists full VM memory; resume restores it. Note GPU passthrough VMs **don't snapshot-resume cleanly** (device state isn't captured), so idle GPU sessions are instead **checkpointed to the workspace and torn down** — GPU is too expensive to leave resident anyway. - **Resume (CPU substrate):** restore the snapshot (fast — memory is loaded back, no re-boot, no re-`import`), re-attach networking with fresh credentials/entropy. Resume from a local snapshot is much faster than from object store, so keep recent snapshots warm-local and tier older ones off. - **Terminate:** flush final files/logs, destroy the runtime, release the overlay (after a durable checkpoint), revoke tokens. **Cold-start strategy:** maintain **warm pools** of pre-booted base microVMs per language/image so the request path is "claim + attach", not "boot". Pool size is autoscaled off arrival rate. For popular notebook stacks, keep **post-import snapshots** (kernel already started, heavy libs imported) so the user's first cell is instant. **Quotas / limits:** per-user/org caps on concurrent sessions, vCPU, RAM, GPU count, and wall-clock for free tier; hard idle timeouts to reclaim resources. --- ### 6) Data persistence Three distinct durability classes — conflating them is the common mistake: | Class | Where | Durability | Resume cost | |---|---|---|---| | **Workspace files** (code, notebooks) — source of truth | Versioned **object store** (optionally git-backed) | Durable, survives session death | n/a — fetched on attach | | **Runtime overlay** (installed pkgs, caches) | Local ephemeral disk + periodic snapshot | Best-effort; rebuildable | Restore snapshot (fast) or re-install (slow) | | **Live VM memory** (CPU suspend/resume) | Firecracker snapshot, local then tiered | Best-effort | Restore = near-instant interactivity | **Strategy:** - **Object store is the durable home** of workspace files. The Runtime Agent autosaves notebooks/files and checkpoints to object storage on a timer and on suspend/terminate. So even if a host dies, the user loses at most the last few seconds of unsaved edits — never their project. - The **overlay** (packages, caches) is treated as a cache: persisted via snapshot for fast resume, but reconstructable from the workspace's environment spec if lost. - **Paid tier** can get a **persistent network volume** mounted per workspace for large datasets, accepting the throughput/contention tradeoff vs. cheap object-store + local-disk for free tier. **Tradeoff to state explicitly:** network volumes make resume trivial but can bottleneck and add per-IOPS cost; snapshot-to-object-store is cheaper at steady state but adds resume latency. Default to object-store + local ephemeral + snapshots; offer volumes as an upgrade. --- ### 7) Execution model Inside each runtime, the **Runtime Agent** is the platform's trusted entrypoint. It supervises a language kernel (a **Jupyter-style kernel protocol** internally is a clean fit — execute requests, stream results, interrupt) but the design stays kernel-agnostic. It exposes an internal (control-plane-only) API: - `POST /execute` — run a cell/command; returns an `execution_id`, streams output via the agent→gateway channel. - `POST /interrupt` — SIGINT the running cell (Ctrl-C). - `GET /health` — kernel + resource status (for idle detection and liveness). - `GET /fs/{path}` / `PUT /fs/{path}` / `DELETE /fs/{path}` — file ops (upload/download, list). Output is captured as a typed stream so the UI can render **rich output**: `stdout`, `stderr`, structured events (`cell_started`, `cell_finished`, `exit_code`), and MIME-typed payloads (text, image/png, HTML) for plots and tables. **Rich-output sizing (don't inline large blobs).** Plots/tables can be large, so they're *not* shoved raw onto the same low-latency text stream. The agent applies a per-payload threshold (e.g., ~256 KB): below it, the MIME payload rides the stream inline; above it, the agent **writes the blob to object store and emits a small reference event** (`{mime, object_key, size, seq}`) that the browser fetches out-of-band. This keeps a single large figure from blowing the streaming buffer or stalling stdout, and is also why the truncation logic in §8 is framed around the text/event stream, not multi-megabyte images. --- ### 8) Log / output streaming (near-real-time, resumable) Goal: low-latency stdout/stderr + structured events to the browser, survivable across reconnects, with bounded buffering. **Path:** 1. Runtime Agent captures kernel output into a **local bounded ring buffer**, tagging each chunk with `(execution_id, stream, seq)` where `seq` is a **monotonic per-stream sequence number**. 2. Agent pushes chunks to the **Streaming Gateway** over gRPC (internal network only — runtimes are never exposed to the internet). 3. Gateway authenticates the browser's WSS connection (scoped session token), then **fans out** chunks to the client. It also **asynchronously persists** output to a durable log store (debugging + audit), decoupled from the live path so log-store latency never stalls the user. 4. Browser renders incrementally; ack'd `seq` lets the gateway advance. **Gateway scaling, state, and reconnect routing.** The gateway is the one *stateful* tier (it holds each session's replay buffer and the agent↔client routing), so the "stateless control plane" claim in §2 explicitly does **not** cover it. It scales horizontally with **consistent-hash sharding by `session_id`**: a session's agent stream and its browser WSS both map to the same gateway shard, so fan-out and replay are local to one instance. The browser is given the **shard-stable endpoint** (via the session record / a routing layer that hashes `session_id`), so a reconnecting client lands back on the **same shard that holds its ring buffer**. If that shard has restarted or the buffer was evicted, the gateway **rehydrates from the durable log store** using `last_seq` before resuming live fan-out — so a shard failover degrades to a slightly slower replay, never lost output. At ~5k concurrent WSS this fits comfortably in a handful of shards; the shard ring is the autoscaling unit. **Delivery semantics & robustness:** - **Reconnect/replay:** on reconnect the client sends `last_seq`; the gateway replays missing chunks from its buffer (or the durable log if evicted). Delivery is **at-least-once**, idempotent on the client via `seq` dedup — the user never silently loses output mid-run. - **Backpressure:** if the client is slow or gone, buffer up to a cap (e.g., a few MB / N seconds) of *text+events*, then drop the oldest with an explicit `"... output truncated ..."` marker rather than OOMing the gateway or stalling execution. Execution keeps running. (Large rich-output blobs don't count against this cap — they're sideloaded per §7.) - **Output floods:** the agent rate-limits/caps total output per execution (a `while True: print()` loop must not DOS the pipeline) and emits a truncation event. - **Multiplexing:** one WSS per session with logical channels (`stdout`, `stderr`, `events`) so the client demuxes cleanly. - **Decoupling running from watching:** if the user disconnects, the cell keeps running (policy choice — matches Colab-style behavior); output buffers and the idle timer governs reclamation. A gateway (vs. direct runtime→browser) centralizes auth, rate limiting, TLS termination, protocol translation, and keeps runtimes off the public internet. --- ### 9) Data model (control plane) ```sql workspace(id, owner_id, org_id, name, env_spec, default_image, created_at, last_active_at) session(id, workspace_id, host_id, runtime_id, substrate /*microvm|gpu_vm|gpu_container*/, state, image, cpu, mem, gpu, gateway_shard, token_id, created_at, idle_since, expires_at) snapshot(id, session_id, kind /*disk|mem*/, location, size_bytes, created_at) file_index(workspace_id, path, object_key, version, size, checksum, updated_at) quota(subject_id /*user|org*/, max_concurrent, max_vcpu, max_mem, max_gpu, tier) audit_event(id, actor, action, session_id, image, net_policy, ts) ``` (`substrate` records which compute path served the session; `gateway_shard` lets a reconnecting client be routed back to the gateway instance holding its buffer.) --- ### 10) API sketch (control plane + streaming) ``` # Workspaces & files POST /v1/workspaces -> create workspace GET /v1/workspaces/{id}/files -> list files PUT /v1/workspaces/{id}/files/{path} -> upload GET /v1/workspaces/{id}/files/{path} -> download # Sessions (lifecycle) POST /v1/workspaces/{id}/sessions -> create/attach runtime {cpu,mem,gpu,image} (gpu>0 routes to the GPU substrate/pool) GET /v1/sessions/{sid} -> state + connection info (incl. gateway endpoint) POST /v1/sessions/{sid}:suspend -> snapshot + free host resources (CPU substrate) POST /v1/sessions/{sid}:resume -> restore snapshot POST /v1/sessions/{sid}:terminate -> checkpoint + destroy # Execution (proxied through gateway to runtime agent) POST /v1/sessions/{sid}/execute {code} -> {execution_id} POST /v1/sessions/{sid}/interrupt # Output stream (WebSocket; endpoint is shard-stable for this session) GET wss://gw/v1/sessions/{sid}/stream?token=...&last_seq=... server frames: {type: stdout|stderr|event, execution_id, seq, mime, data} large rich-output frames carry {object_key,size} instead of inline data ``` --- ### 11) Scheduling, autoscaling, and fairness - **Admission control** in Session Manager: reject/queue at quota boundaries before any host work. - **Substrate routing:** the requested `gpu` count selects the pool. `gpu == 0` → CPU microVM fleet; `gpu > 0` → GPU host pool (passthrough VM or single-tenant hardened container). This is decided at admission so a GPU request never lands on a Firecracker host that can't satisfy it. - **Scheduler bin-packs** sessions onto hosts by `(cpu, mem, gpu)` with separate pools — **CPU-only, GPU (single-tenant), high-mem** — so a GPU session doesn't strand CPU capacity and vice versa. Prefer hosts with a warm runtime of the requested image (locality cuts cold start). - **Autoscaling:** scale each host pool off its own active+queued demand and warm-pool depletion. Suspended CPU sessions consume no host CPU/RAM (only snapshot storage), so the CPU fleet scales with *active* load, not registered users. GPU capacity is scaled and budgeted independently since idle GPU sessions are torn down rather than suspended. - **Fairness:** weighted fair sharing per org; for free tier, preempt-to-suspend the longest-idle CPU sessions when capacity is tight (their state is snapshotted, so preemption is non-destructive). GPU is gated by hard quotas rather than oversubscribed. Hard wall-clock and idle caps on free tier. --- ### 12) Observability **Metrics (SLIs):** session-start latency p50/p95 (warm vs. cold, per substrate), warm-pool hit rate, resume latency, streaming lag and dropped-chunk rate, running/idle/suspended counts, CPU vs GPU host utilization, noisy-neighbor / preemption events, snapshot size & age, gateway shard fan-out and rehydration rate. **Tracing:** end-to-end span on `create session → admit → choose substrate → schedule → claim runtime → attach overlay → agent-ready`, so cold-start regressions are attributable to a stage. **Audit logs (security-critical):** who started which session with which image, substrate, and network policy; **denied egress attempts**, metadata-endpoint hits, token mints/uses, suspicious-syscall signals. These feed abuse detection and incident response. --- ### 13) Failure modes & edge cases - **Cold start too slow** → warm pools + post-import snapshots + small base images + image caching on hosts (GPU warm pools are smaller and coarser due to cost). - **GPU requested but no Firecracker support** → handled at admission: `gpu > 0` is routed to the GPU substrate/pool (passthrough VM or single-tenant container), never to a microVM. - **Host dies mid-session** → workspace files are already durable in object store (autosave + checkpoint); user reconnects and a new session re-attaches the workspace. In-flight unsaved output is best-effort from the durable log. - **Gateway shard dies / buffer evicted** → reconnecting client is re-routed to the (restarted or replacement) shard for its `session_id`, which rehydrates the replay buffer from the durable log using `last_seq` before resuming live fan-out. - **User installs huge deps / fills disk** → overlay disk quota + cached package layers; OOM-kill the cell, not the host. - **Infinite output / fork bombs / crypto-mining** → output caps, cgroup CPU/PID limits, egress denial + abuse signals → throttle or terminate. - **Disconnected client** → execution continues; idle timer governs suspend; output replayable on reconnect. - **Snapshot reuse leaking entropy/identity** → re-seed RNG and mint fresh per-session credentials on every restore. - **Secrets leakage** → never mount broad cloud credentials; scoped per-session tokens only; metadata endpoint blocked *and* IMDSv2-enforced. --- ### 14) Optional: collaboration Separate the two concerns: - **Document state** (who's typing what) → a **CRDT** (or OT) sync service over WebSocket, independent of the runtime. This scales to many viewers/editors cheaply. - **Execution** → keep a **single owner kernel** per notebook (one runtime) to avoid contention and permission ambiguity; collaborators see streamed output via the same gateway fan-out but exec requests are owner-gated (or queued). Trying to share one runtime symmetrically across users creates resource-contention and trust problems, so the clean model is "shared editing, single-owner execution," with per-user runtimes if true parallel execution is needed. --- **Summary:** microVMs (Firecracker, run under `jailer`) as the **default CPU** untrusted-execution substrate, with GPU sessions routed to a **separate single-tenant pool** running full VMs with PCIe passthrough (or hardened NVIDIA-stack containers) because Firecracker has no PCI bus; layered isolation (own guest kernel, read-only base + quota'd overlay, default-deny egress with the metadata endpoint blocked *and* IMDSv2-enforced, no node credentials + short-lived scoped tokens); warm pools + snapshot/restore for sub-second-to-few-second start and near-zero-cost idle on the CPU fleet; object-store as the durable home for workspace files (with large rich-output blobs sideloaded there too); and a resumable, backpressure-aware, sequence-numbered streaming pipeline through a **session-sharded** internal gateway so output is real-time, lossless-on-reconnect, and the runtimes never face the internet.

Related Interview Questions

  • Design a Highly Available Conversational AI Service - OpenAI (medium)
  • Design an Online Chess Service from Launch to Scale - OpenAI (medium)
  • Design a Fault-Tolerant Video Generation Platform - OpenAI (hard)
  • Design A ChatGPT-Style Assistant Product - OpenAI (medium)
  • Design a Tier-Aware AI Video Generation Service - OpenAI (medium)
|Home/System Design/OpenAI

Design a sandboxed cloud IDE

OpenAI logo
OpenAI
Jan 22, 2026, 12:00 AM
easySoftware EngineerOnsiteSystem Design
368
0

System Design: Sandboxed Cloud IDE (Colab-like)

Design a multi-tenant, browser-based cloud IDE/notebook that lets users run code inside an isolated sandbox (similar to a hosted notebook product such as Colab). The defining challenge is safely executing arbitrary, untrusted user code at scale while keeping the experience interactive.

Core User Experience

  • A user opens a workspace (project/notebook), edits code in the browser, and runs cells or shell commands.
  • Output appears in the UI: stdout / stderr plus rich output (plots, tables, images).
  • Users can watch streaming logs while their code is still running.

Requirements

Functional

  • Provision an isolated compute environment per workspace/session.
  • Execute arbitrary user code safely (sandboxing).
  • Stream execution output and logs to the browser in near real time.
  • Support basic file operations (upload/download, persisted workspace state).
  • Basic real-time collaboration is optional — call it out explicitly if you choose to include it.

Non-functional

  • Strong isolation between tenants — security is the primary, non-negotiable constraint.
  • Reasonable startup latency when launching a new session.
  • Support autoscaling and fair resource sharing across users/orgs.
  • Observability: metrics, tracing, and audit logs.

Focus Areas to Cover

  1. Compute substrate — how you choose and manage it (VMs vs. containers vs. microVMs vs. userspace-kernel sandboxes), including the GPU case.
  2. Isolation model — filesystem, network, process/kernel, and credentials.
  3. Log/output streaming architecture — near-real-time, resumable, backpressure-aware.
  4. Lifecycle management — create, run, idle, suspend/resume, terminate.
  5. Data persistence strategy — workspace files, runtime overlay, checkpoints/snapshots.

Please state your assumptions, provide an API sketch, and describe a high-level architecture (a diagram described in text is fine).

Clarifying Questions to Ask Guidance

A strong candidate scopes the problem before designing. Good questions here include:

  • Scale: How many registered users vs. concurrent active sessions at peak? How large is the idle/suspended tail relative to active sessions?
  • Workload shape: Are sessions bursty and interactive (short cells, long idle gaps), or long-running batch jobs? Which languages/runtimes must we support first?
  • GPU: Do we need to support GPU/accelerator sessions, and roughly what fraction of sessions?
  • Trust & threat model: Is all user code untrusted by default? What's the blast radius we must contain (single tenant, single host, whole fleet)?
  • Startup SLA: What's the acceptable cold-start latency for a fresh session (sub-second? single-digit seconds?)?
  • Persistence expectations: Must workspace files survive host death? Is there a free vs. paid tier with different durability/quotas?

Constraints & Assumptions

Use these to anchor the design (adjust if you prefer, but state your numbers):

  • Untrusted by default — assume every session may be a hostile attacker.
  • A session is single-user ; multiple sessions belong to a workspace.
  • Workloads are bursty and interactive : short cells, long idle gaps; mostly Python/JS, with a minority needing a GPU.
  • Order-of-magnitude scale to design against: roughly 100k registered users , on the order of a few thousand concurrent active sessions at peak, plus a much larger idle/suspended tail (≈10× active).
  • A typical session ≈ 1–2 vCPU, 2–4 GB RAM ; GPU sessions are the exception, not the norm.
  • Cold-start target: a fresh session should become interactive in single-digit seconds at p95.
  • You control a cloud fleet (bare-metal / nested-virt-capable hosts) and can run a virtualization layer.

What a Strong Answer Covers Guidance

This lists the dimensions the interviewer is grading — not the answers themselves:

  • Capacity estimate: active vs. idle sizing, host count, and what the idle tail implies for cost.
  • Substrate choice with explicit tradeoffs: isolation strength vs. cold-start vs. density across container / hardened-container / userspace-kernel / microVM / full-VM — and a clear default with justification .
  • The GPU exception: recognizing the default substrate may not support accelerators, and routing GPU sessions appropriately.
  • Layered isolation: process/kernel, filesystem, network egress, and credential/identity handling — defense in depth, no single fatal bypass.
  • Streaming design: low latency, ordering, reconnect/replay, backpressure, and keeping runtimes off the public internet.
  • Lifecycle & cold-start strategy: warm pools, suspend/resume, idle reclamation, quotas.
  • Data durability classes: distinguishing source-of-truth files from rebuildable runtime state.
  • Scheduling, autoscaling, fairness, and observability (including security-relevant audit signals).
  • Failure modes: explicit handling of crashes, floods, abuse, and snapshot/identity hazards.
  • Communication: stated assumptions, a clean API sketch, and a coherent architecture narrative.

Follow-up Questions Guidance

Be ready for deeper probes after the main design:

  • Scale-out: What breaks first as you go from a few thousand to 100k concurrent active sessions ? Which tier is the bottleneck — scheduler, gateway, or the host fleet?
  • GPU isolation: If your default substrate can't attach a GPU, how do you isolate GPU sessions while still exposing the device — and how do you bound the blast radius?
  • Streaming failover: A streaming gateway shard dies mid-run with the client mid-stream. Exactly how does the reconnecting client recover without losing output?
  • Abuse at scale: A coordinated set of free-tier users runs crypto miners and fork bombs. Which controls (cgroups, egress, abuse signals) trip, and what's the automated response?

Submit Your Answer to Earn 20XP

Sign in to leave a comment

Loading comments...

Browse More Questions

More System Design•More OpenAI•More Software Engineer•OpenAI Software Engineer•OpenAI System Design•Software Engineer System Design

Your design canvas — auto-saved

PracHub

Master your tech interviews with 9,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.