V2 Sandboxes

Modal’s next-generation Sandbox backend has a number of advantages over the existing backend:

  • Create sandboxes with higher throughput
  • Lower time-to-interactive
  • Run more sandboxes concurrently

The new backend is recommended to users wanting:

  • >20 sandbox creates per second
  • >10,000 concurrent sandboxes

To use it, call Sandbox._experimental_create instead of Sandbox.create. Beyond that, the interface is exactly the same. Most features are supported, but a few are not — see the Feature support section below for details. If all features you use are supported, changing the “create” method is the only change you’ll need to make!

Example usage 

Make sure you are on the latest version of the Modal client:

uv pip install --upgrade modal

All of the core Sandbox operations — exec, wait, poll, terminate, reading stdout/stderr, and writing to stdin — work the same way as they do in the standard Sandbox API:

sb = modal.Sandbox._experimental_create(
    "sleep", "300",
    app=sb_app,
    cpu=2,
    memory=4096,  # MiB
)

p = sb.exec("python", "-c", "print('hello world')")
print(p.stdout.read())
p.wait()
assert p.returncode == 0

sb.terminate()

Feature support 

FeatureStatusNotes
Exec✅ Supportedsb.exec(...)
Stdin / stdout / stderr✅ SupportedStream I/O with running processes.
CPU and memory configuration✅ Supportedcpu and memory on create.
Custom images✅ SupportedAny modal.Image via the image parameter.
Secrets and environment variables✅ Supportedsecrets and env on create, or per-exec.
Volumes✅ Supportedmodal.Volume via the volumes parameter.
Reload volumes✅ Supportedsb.reload_volumes() blocks until the reload completes; pass a timeout (default 55s) to bound the wait. Not supported on VM Sandboxes.
Cloud bucket mounts✅ Supportedmodal.CloudBucketMount with static credentials or OIDC.
Encrypted tunnels✅ Supportedencrypted_ports on create, sb.tunnels() to retrieve.
Custom domains✅ Supportedcustom_domain on create; tunnel hostnames become subdomains of your domain. Requires prior setup by Modal — see Custom domains.
Filesystem API✅ SupportedRead and write files via sb.filesystem — see Filesystem access. Requires Python SDK ≥ 1.5.2.dev4 (a nightly build, until 1.5.2 is released — see above). The deprecated sb.open() / sb.ls() / sb.mkdir() / sb.rm() / sb.watch() methods are not supported.
Filesystem snapshots✅ Supportedsb.snapshot_filesystem().
Directory snapshots✅ Supportedsb.snapshot_directory().
Region placement✅ Supportedregion parameter.
Network control✅ Supportedblock_network and CIDR allowlists.
Private networking (i6pn)✅ Supportedi6pn=True; pin sandboxes to the same region. See Private networking.
Names✅ Supportedname parameter on create.
OIDC identity tokens✅ Supportedinclude_oidc_identity_token, oidc_auth_role_arn on CloudBucketMount.
Proxies✅ Supportedproxy parameter.
VM runtime✅ Supportedexperimental_options={"vm_runtime": True}. See VM Sandboxes.
Reattach by ID✅ SupportedStore sb.object_id; use Sandbox.from_id(id). See below.
Listing sandboxes✅ SupportedSandbox._experimental_list(app_id=...). Sandbox.list() does not return V2 sandboxes. Tags are supported.
GPUs❌ Not yet
Memory snapshots❌ Not yetFilesystem snapshots do work.
Sandbox.from_name()❌ Not yetUse Sandbox.from_id(sb.object_id) instead.
modal shell (CLI)❌ Not yetConnecting to a running V2 Sandbox via modal shell <sandbox-id> / <task-id> is not supported; use sb.exec(...) instead.
Connect tokens❌ Not yet
Unencrypted tunnels❌ Not yetUse encrypted_ports instead.
Network file systems❌ Not yet

Retrieving a Sandbox by ID 

Because Sandbox.from_name() does not work with V2 Sandboxes, store the object_id after creation and use Sandbox.from_id to reattach:

sb = modal.Sandbox._experimental_create("sleep", "300", app=sb_app)
sandbox_id = sb.object_id
print(f"Sandbox ID: {sandbox_id}")

# Later, reattach:
sb2 = modal.Sandbox.from_id(sandbox_id)
p = sb2.exec("echo", "reattached")
print(p.stdout.read())
p.wait()
sb2.terminate()

Miscellaneous 

  • When modal.Sandbox._experimental_create() returns, the sandbox is already scheduled onto a machine (though not yet ready). This differs from v1 semantics, where create() returns before the sandbox is scheduled. As such, once the _experimental_create() call returns, you can expect a shorter, more consistent wait_until_ready:
    sb = modal.Sandbox._experimental_create(app=app, readiness_probe=some_probe)
    sb.wait_until_ready()
    You can read more about the sandbox lifecycle in the sandbox documentation.

If you hit a rough edge that isn’t listed here, please reach out via Slack or email us at support@modal.com.