Skip to content

Changelog

27 October 202623 changes

SDKs and CLI 5 · Sandboxes 13 · Platform 2 · Apps 1 · CI/CD 2

9 min read

NewerOlderFollow by RSS

A job's runs in the v2 API and every SDK

GET /v2/jobs/{id}/runs returns a job's runs, newest first and paginated, and GET /v2/job-runs/{id} returns one run: its execution, task, attempt, state, exit code and times. The SDKs read them with jobs.runs(id) and jobs.run(id) (Runs and Run in Go).

Connections between your sandboxes are no longer reset in bursts

Connections by name (<name>.sandbox.internal) were refused past 100 new connections a second across the whole account, so a sandbox opening several at once saw some of them reset (about 17% with 8 at once). The limit is now 1,000 a second per account and 500 a second per target sandbox, and a connection over it waits for its turn instead of being refused. A sandbox created moments ago is also reachable within about a second; before, it could be refused with private-network-off or private-network-no-sandbox for up to 15 seconds. See networking.

OpenTelemetry exports in the v2 API

The v2 API adds an otel-exports resource, with matching methods in the TypeScript, Python, Go, Java and Ruby SDKs. An export sets endpoint (an OTLP/HTTP base URL), signals (logs, metrics, traces) and enabled. headers are write-only: they are sent with every push and never returned; status.headerNames lists their names. An update that sends headers replaces all of them. A project can have at most three exports. The v1 OpenTelemetry export API returns the same responses as before.

Image builds check variable names in the request schema

A build request to POST /v1/images returns 400 invalid_request when a key of env, recipe.env or buildArgs is not a valid variable name (a letter or underscore, then letters, digits or underscores, up to 256 characters). details.issues names each such key. Builds already refused these names; the request is now refused before it is read further, and the published OpenAPI document declares the rule as propertyNames.

Webhooks in the v2 API

The v2 API adds a webhooks resource, with matching methods in the TypeScript, Python, Go, Java and Ruby SDKs. A webhook sets url, events, enabled and description. Increasing spec.secretGeneration by one rotates the signing secret; the previous secret keeps signing for spec.keepPreviousSeconds. The secret is returned only in the response to the create request and to each rotation. The v1 Webhooks and Events APIs return the same responses as before.

Run virtual machines inside a sandbox with /dev/kvm

New sandboxes on current-generation hosts boot a guest kernel with KVM and expose /dev/kvm to every user in the sandbox, so QEMU, Firecracker or an Android emulator runs with hardware acceleration instead of emulation. A VM inside a sandbox keeps running through pause and resume, and is carried into snapshots and forks. To check whether a sandbox has it, run test -c /dev/kvm && echo yes. Sandboxes created before this change keep their kernel until they are recreated.

Waking a sandbox keeps the egress CA its running processes trust

A sandbox started before 26 October 2026 and woken afterwards was given a new egress CA, and processes that were already running (Node.js with NODE_EXTRA_CA_CERTS, Bun, Python, Go) failed HTTPS requests that use egress secrets with SELF_SIGNED_CERT_IN_CHAIN until they were restarted. A wake now keeps the CA the sandbox already trusts and writes nothing to its trust store. See security.

Accepting an invitation adds you only to the account that invited you

A person whose first sign-in was through an invitation link was also given a separate account in their own name. Opening an invitation now creates nothing; you join the inviting account when you select Join. See teams.

Sandbox usage

GET /v2/sandboxes/{id}:usage?month=YYYY-MM returns what a sandbox used in a calendar month (UTC): its metered periods, milliseconds running, vCPU-microseconds billed, MiB-milliseconds of memory, and the amount charged, in microdollars. Without month, it returns the current month. It requires the same permission as reading the sandbox, and is available as sandboxes.usage(id, month) in the TypeScript, Python, Go, Java and Ruby SDKs, as runtime sandboxes usage <id> [--month YYYY-MM] in the CLI, and as an MCP tool.

Volumes on v2 Apps, Functions and Containers

spec.volumes lists up to 16 volumes, each with volume, path and mode. A volume is mounted at any absolute path outside the system folders (such as /var/lib/postgresql/data) before a copy's process starts. With mode: write (the default), one copy mounts the volume read-write, so the service runs one copy and status.copyLimit is 1, with status.copyLimitReason naming the volume. A new version, a rollback or a restart stops the old copy (SIGTERM, then stopGraceSeconds, default 30) and unmounts the volume before the new copy mounts it. With mode: readMany, each copy mounts its own read-only copy-on-write copy of the volume as it was when the version was made, and the service scales as usual. A volume another sandbox mounts read-write fails the version with volume_attached; deleting a service never deletes a volume.

A sandbox at its memory limit stays reachable, and every process killed for memory is recorded

Linux's out-of-memory killer now never ends Runtime's agent or the sandbox's network relay, so commands keep running after a kill. Each process it ends, inside a command or not, is recorded as the event sandbox.oom_kill with its name, process ID and memory. The event is shown on the sandbox's page in the console and sent to webhooks. When the agent does not answer, the API returns sandbox_unreachable (503, retryable), and names a recent out-of-memory kill when there was one. When the sandbox cannot read its environment, the API returns sandbox_environment_unavailable (503, retryable). Both replace guest_busy, which now means only that the sandbox is running as many commands as it can. POST /v1/sandboxes/{id}:restart on a running sandbox now reboots it from its disk instead of returning 409 not_resumable. See troubleshooting.

Relative file paths work in the compatibility layers, and Vercel's stop() sends one request

Paths such as ./hello.txt or src/../a.txt passed to the CodeSandbox, Cloudflare, E2B, Daytona and other compatibility layers now resolve against the layer's working folder (/project/sandbox for CodeSandbox), as the vendors' SDKs resolve them. Before, the API returned 400 with invalid_request for a path containing . or ... In withruntime/vercel, sandbox.stop() no longer reads the sandbox first: it pauses or stops it in one request, saving one round trip per stop.

Volumes and backups in the v2 API; snapshots no longer pause the sandbox

The v2 API adds volumes and backups resources, with matching methods in the TypeScript, Python, Go, Java and Ruby SDKs, the CLI and the MCP server. A volume sets dailyBackups and backupRetentionDays; a backup names its volume and reports sizeMib and expiresAt. A v2 snapshot now takes mode (memory or disk) and retentionDays, and reports expiresAt, vcpus, memoryMib and diskMib. Taking a snapshot of a running sandbox no longer pauses it: the sandbox keeps running while the snapshot is made. The v1 Volumes and Snapshots APIs return the same responses as before.

Connections between your sandboxes are no longer held to upload limits

Connections by name (<name>.sandbox.internal) were limited to 100 Mbit/s per sandbox, and to 20 Mbit/s after the first 2 GiB, as uploads to the internet are. They are now limited only by each server's share of the private network. They do not count toward a sandbox's upload limits or its daily network amount. Each sandbox can still be reached for 100 GiB a day. See networking.

CI/CD jobs stay queued until a machine begins them

A job is now running only once one of its machine's tasks starts, and sandboxIds lists each sandbox as soon as it begins the job, not only when the job ends. While a job is queued, its reason says what it waits for (for example, "Waiting for a machine: the check's machines are starting, or running other jobs' tasks"), the console shows how long it has waited, and the check's runtime run on GitHub says how many jobs are waiting and for how long. Previously a job could report running for many minutes while its tasks waited behind other jobs' tasks.

Sandboxes in a compartment reach each other by name

With the private network on, sandboxes created by keys in the same compartment can now connect to each other at <name>.sandbox.internal or <id>.sandbox.internal. Before, every connection from a compartment's sandbox was refused with private-network-off. Compartments stay walled: a sandbox reaches only sandboxes of its own compartment, or, outside every compartment, only the account's other sandboxes outside every compartment. A name across the wall returns 502 with private-network-no-sandbox.

Requests through a sandbox's web proxy no longer wait 40 ms on responses

The sandbox's web proxy relay (127.0.0.1:10800) and guest-egress.py forward now set TCP_NODELAY on the program's connection. Previously the kernel held the last segment of any response longer than one segment until the program acknowledged the one before, and most clients delay that acknowledgement by 40 ms. A call to Runtime's API from a sandbox that returns more than about 1.4 KB (creating, pausing or waking a sandbox, reading a file, listing sandboxes) completes about 40 ms sooner, as does any other request made through the proxy. A sandbox uses the new relay from its next boot, or from the wake after its next pause. The transparent relay, used by programs that ignore HTTPS_PROXY, gets the same change with the next base image.

Speed figures are published only from live measurements

The speed page, the guides, the website, llms.txt and the MCP server's instructions no longer quote the sandbox timings measured in September 2026. The speed page now lists only operations measured by the outside probe each minute whose own time is within 2× of their physical floor, with p50, p95, p99, sample counts and the raw samples. The disk, network and trace overhead measurements moved to observability.

Change a key's reach or daily limit over the API

PATCH /v1/keys/{id} (runtime.keys.update in the TypeScript SDK, runtime keys update in the CLI) changes a key's reach (agent or account) or dailyLimitMicros (null removes the limit). A key can widen only the keys it made, never past its own reach or daily limit, and returns 403 forbidden otherwise. It can narrow any key it sees, and it never changes itself. Each change is recorded in the audit log. Previously only the website could change an existing key.

CI/CD reports a job lost with its machines as "Could not run"

When a job's task is lost on three machines without reporting a result, the job now ends as Could not run with the reason (for example, "Lost 3 times, never reported; the last on : no answer for 265 s"). Previously it was reported as "Failed in 0.0 s: exit code 1" and annotated on a file it never tested. A test failure in the same job still fails it. Lost runs are no longer recorded as test failures in flaky-test history.

Sandboxes reach each other by name on every server

A sandbox can now connect to another sandbox in its organization at <name>.sandbox.internal whichever server runs either one; before, only sandboxes on one server could. Private networks (WireGuard) are reachable from sandboxes on every server too. Connections by name have a daily allowance of 100 GiB for each target sandbox, separate from the private network's 50 GiB. When the private network is briefly unavailable from a server, connections to it return 503 with private-network-unavailable and are never sent anywhere else; retry after a minute.

The speed page is measured every minute, from outside, with the raw samples

Runtime now times its operations once a minute (volumes every five minutes) through the public API, from a fixed location outside Runtime (a Cloudflare Durable Object in eastern North America), and reports Runtime's own time and the network's part separately, with p50, p95, p99 and sample counts for the last 24 hours. An operation is listed once its own time is within 2× of its physical floor, the least time its work can take on today's hardware. The summary is at https://status.withruntime.com/speed.json and each day's samples at https://status.withruntime.com/speed/samples.jsonl?day=YYYY-MM-DD (speed).

Every API response includes a Server-Timing header

The header, Server-Timing: app;dur=12.3, gives the time in milliseconds from when the API received the request until its response headers were ready. Subtract it from the time your client measured to get the network's share of each call (the API).

Other days.

Cut your cloud billby 75%.

No card to start. Billed by the second after.

Start free, no card