Isolation
Daytona sandboxes are isolated by default. Code running in a sandbox cannot read another sandbox’s filesystem or memory, is not on a shared network with other sandboxes, and its credentials and API access are scoped to its own organization.
Isolation operates at three boundaries:
| Boundary | What is separated | Mechanisms |
|---|---|---|
| Runtime | Processes, filesystem, memory, and devices of each sandbox | • Sandbox classes • Reserved resources |
| Network | Traffic entering and leaving each sandbox | • Network limits • Preview authentication • Link networks |
| Organization | Access to sandboxes, data, and credentials | • Organizations • API key permissions • Secrets |
Runtime isolation
Section titled “Runtime isolation”Runtime isolation separates what runs inside one sandbox from the runner it executes on and from every other sandbox. Each sandbox runs as an isolated instance with its own processes, network, filesystem mounts, and inter-process communication: see architecture for details.
Resources are part of the runtime boundary. Each sandbox reserves its own vCPU, memory, and disk, enforced as hard limits, so one sandbox cannot consume the resources of another regardless of what its code does. Sandbox classes differ in the kind of boundary they provide:
| Sandbox class | Runtime boundary |
|---|---|
| Container | Isolated container with dedicated namespaces and enforced resource limits. Code runs as root inside the sandbox without affecting the runner. |
| VM sandboxes (Linux VM and Windows) | Full virtual machine with its own kernel. The hardware virtualization boundary enables VM-only capabilities: pause / resume, fork, and hot snapshots. |
| GPU | Isolated container with exclusive GPU allocation: assigned GPU devices belong to one sandbox at a time and are never shared. |
Resource limits are visible inside the sandbox through cgroup values. Tools such as nproc and free read host-level values and do not reflect the sandbox’s own limits:
cat /sys/fs/cgroup/cpu.max # "<quota> <period>" (cores = quota / period)cat /sys/fs/cgroup/memory.max # bytesdf -h / # diskNetwork isolation
Section titled “Network isolation”Network isolation controls traffic in each direction separately. Outbound and inbound access are configured per sandbox; sandbox-to-sandbox networking is off unless sandboxes are explicitly linked.
| Direction | Default | Controls |
|---|---|---|
| Sandbox to internet | Open on Tier 3 and above; restricted on Tier 1 and 2 | Network limits: block all, CIDR allow list, or domain allow list |
| Internet to sandbox | Authenticated preview URLs and SSH access | Preview tokens and signed URLs and SSH tokens; the public flag opts previews out of authentication |
| Sandbox to sandbox | No shared network | Linked sandboxes join a parent and its children into a link network |
Outbound traffic passes a per-sandbox firewall. Tier-based restrictions apply automatically, and each sandbox can be locked down further with one of three mutually exclusive settings: block all traffic, allow specific CIDR ranges, or allow specific domains. Essential services such as package registries stay reachable on all tiers.
Inbound traffic reaches a sandbox through preview URLs or SSH access, and both paths are authenticated: preview URLs require a preview token or a signed URL unless the sandbox is explicitly made public, and SSH connections require an SSH access token.
Between sandboxes, there is no shared network. Linked sandboxes are the deliberate exception: children are scheduled onto the same runner as their parent and joined into a link network where each sandbox is reachable by name, while remaining isolated from every sandbox outside the group.
from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
# Block all outbound trafficsandbox = daytona.create(CreateSandboxFromSnapshotParams( network_block_all=True,))
# Or allow specific domains onlysandbox = daytona.create(CreateSandboxFromSnapshotParams( domain_allow_list="example.com,*.daytona.io",))from daytona import Daytona
daytona = Daytona()sandbox = daytona.create()
# Preview URLs require a token unless the sandbox is publicpreview = sandbox.get_preview_link(3000)print(preview.url) # https://3000-{sandboxId}.{proxy-domain}print(preview.token) # sent via the x-daytona-preview-token headerfrom daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
parent = daytona.create()
# Only linked sandboxes share a network; everything else is isolatedchild = daytona.create(CreateSandboxFromSnapshotParams( linked_sandbox=parent.id, ephemeral=True,))
# Sandboxes on the link network are reachable by nameresponse = child.process.exec(f"curl http://{parent.name}:3000/")Organization isolation
Section titled “Organization isolation”Organization isolation separates tenants. Every sandbox, snapshot, and volume belongs to exactly one organization, and access control is enforced at that boundary: an API key from one organization cannot see or operate on another organization’s resources.
Within an organization, access narrows further:
- API key permissions scope what a key can do. A key issued with only
write:sandboxescannot delete snapshots or read volumes. - Managed API keys issue scoped child keys at runtime, so a multi-tenant application can hand each tenant a key limited to its own operations.
- Secrets keep credentials out of sandboxes entirely. A sandbox holds an opaque placeholder; an outbound proxy substitutes the real value only for requests to the secret’s allowed hosts. Code in the sandbox can use the credential but cannot read it or send it anywhere else.
- Volumes scope shared data with a
subpath, so each sandbox mounts only its tenant’s slice of a shared volume.
# A manager key issues a child key scoped to one tenant's operations;# child key permissions must be a subset of the manager key's permissionscurl 'https://app.daytona.io/api/api-keys' \ --request POST \ --header 'X-Daytona-Organization-ID: YOUR_ORGANIZATION_ID' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_MANAGER_API_KEY' \ --data '{ "name": "tenant-a-key", "permissions": ["write:sandboxes", "delete:sandboxes"]}'from daytona import CreateSandboxFromSnapshotParams, Daytona
daytona = Daytona()
# The sandbox receives a placeholder, never the plaintext valuesandbox = daytona.create(CreateSandboxFromSnapshotParams( secrets={ "MY_API_KEY": "my-secret", },))
# Code uses the credential without being able to read itsandbox.process.exec( 'curl -H "Authorization: Bearer $MY_API_KEY" https://api.example.com/v1/data')from daytona import CreateSandboxFromSnapshotParams, Daytona, VolumeMount
daytona = Daytona()volume = daytona.volume.get("tenant-data", create=True)
# Each sandbox mounts only its tenant's slice of the shared volumesandbox = daytona.create(CreateSandboxFromSnapshotParams( volumes=[VolumeMount( volume_id=volume.id, mount_path="/home/daytona/data", subpath="tenants/tenant-a", )],))