Scale
Daytona sandboxes are designed to be created and operated at high volume. A single sandbox scales up by bursting beyond its guaranteed resources, a fleet scales out by running many sandboxes at once, and each sandbox runs multiple processes concurrently.
Scale operates along three dimensions:
| Dimension | What scales | Mechanisms |
|---|---|---|
| Sandbox (vertical) | vCPU, RAM, and disk of a single sandbox | • Custom resources • Resource limits |
| Fleet (horizontal) | Number of sandboxes running simultaneously | • Snapshot fan-out |
| Workload (concurrency) | Concurrent processes in a sandbox | • Sessions • Background commands • PTY sessions |
Sandbox scaling
Section titled “Sandbox scaling”Sandbox scaling sets the resources of a single sandbox: vCPU, memory, and disk. The resource envelope is fixed when the sandbox is created; there is no resize operation.
The requested resources are a guaranteed floor, reserved at placement; beyond it the sandbox uses idle host capacity up to its resource limits, which default to the platform caps.
Size the guarantee for the workload’s average, and let bursting absorb the peaks. For a fixed shape with no bursting, set every limit equal to its request.
Guarantee CPU, memory, and disk when creating a sandbox.
from daytona import Daytonafrom daytona.v2 import Resources
daytona = Daytona()
# Guarantee 2 vCPUs, 4 GiB of RAM, and 16 GiB of disk for the sandboxsandbox = daytona.v2.create( resources=Resources(cpu=2, memory=4, disk=16),)| Parameter | Type | Description |
|---|---|---|
cpu | float | Sets the CPU guarantee in cores. The value can be fractional. The API raises the CPU guarantee to at least 1 core per 4 GiB of memory. |
memory | int | In GiB. |
disk | int | In GiB. |
- The API raises values below the minimums to those minimums.
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
// Guarantee 2 vCPUs, 4 GiB of RAM, and 16 GiB of disk for the sandboxconst sandbox = await daytona.v2.create({ resources: { cpu: 2, memory: 4, disk: 16 },})| Parameter | Type | Description |
|---|---|---|
cpu | number | Sets the CPU guarantee in cores. The value can be fractional. The API raises the CPU guarantee to at least 1 core per 4 GiB of memory. |
memory | number | In GiB. |
disk | number | In GiB. |
- The API raises values below the minimums to those minimums.
# Guarantee 2 vCPUs, 4 GiB of RAM, and 16 GiB of disk; --memory and --disk are MiBdaytona sandbox create --cpu 2 --memory 4096 --disk 16384| Flag | Type | Description |
|---|---|---|
--cpu | float | Sets the CPU guarantee in cores. The value can be fractional. The API raises the CPU guarantee to at least 1 core per 4096 MiB of memory. |
- The API raises values below the minimums to those minimums.
curl 'https://api.daytona.com/sandboxes' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "cpu": 2, "memory_mib": 4096, "disk_mib": 16384}'| Field | Type | Description |
|---|---|---|
cpu | number | Sets the CPU guarantee in cores. The value can be fractional. The API raises cpu to at least 1 core per 4096 MiB of memory_mib. |
memory_mib | integer | In MiB. |
disk_mib | integer | In MiB. |
- The API raises values below the minimums to those minimums.
A sandbox bursts beyond its guarantee up to the platform caps: 16 vCPUs and 32 GiB of memory. The per-sandbox vCPU and memory limits lower those ceilings and cannot raise them. CPU guarantees can be fractional, as small as 0.125 cores. Usage above the guarantee is billed as measured; see billing.
from daytona import Daytonafrom daytona.v2 import Resources
daytona = Daytona()
# Guarantee half a core and 2 GiB, and burst up to 4 vCPUs and 8 GiBsandbox = daytona.v2.create( resources=Resources(cpu=0.5, cpu_max=4, memory=2, memory_max=8),)| Parameter | Type | Description |
|---|---|---|
cpu_max | int | Whole vCPUs from 1 to 16. |
memory_max | float | In GiB, from 256 MiB to 32 GiB. |
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
// Guarantee half a core and 2 GiB, and burst up to 4 vCPUs and 8 GiBconst sandbox = await daytona.v2.create({ resources: { cpu: 0.5, cpuMax: 4, memory: 2, memoryMax: 8 },})| Parameter | Type | Description |
|---|---|---|
cpuMax | number | Whole vCPUs from 1 to 16. |
memoryMax | number | In GiB, from 256 MiB to 32 GiB. |
# Guarantee half a core and 2 GiB, and burst up to 4 vCPUs and 8 GiBdaytona sandbox create --cpu 0.5 --vcpus-max 4 --memory 2048 --mem-max 8192| Flag | Type | Description |
|---|---|---|
--vcpus-max | int32 | Whole vCPUs from 1 to 16. |
--mem-max | int32 | In MiB, from 256 to 32768. |
curl 'https://api.daytona.com/sandboxes' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "cpu": 0.5, "vcpus_max": 4, "memory_mib": 2048, "memory_max_mib": 8192}'| Field | Type | Description |
|---|---|---|
vcpus_max | integer | Whole vCPUs from 1 to 16. |
memory_max_mib | integer | In MiB, from 256 to 32768. |
Fleet scaling
Section titled “Fleet scaling”Fleet scaling raises how many sandboxes run at once. The unit of scale is the sandbox: instead of packing unrelated workloads into one environment, create one per user, task, or agent.
Each sandbox is a hardware-isolated microVM, so a fleet scales linearly: each sandbox adds capacity without contention, and a failure in one does not affect others. Creation is fast and asynchronous, so scale on demand: create when work arrives, delete when done.
Snapshot fan-out starts each unit of work from the memory and disk of one prepared snapshot, so any number of identical environments run from the same state. Snapshot fan-out works with Linux sandboxes. macOS sandboxes start from a disk image instead: see macOS snapshots.
from concurrent.futures import ThreadPoolExecutor
from daytona import Daytona
daytona = Daytona()
def run_task(shard: int) -> str: # Each task gets its own isolated sandbox from the same snapshot sandbox = daytona.v2.create_from_snapshot("my-snapshot") response = sandbox.process.run(f"python3 run.py --shard {shard}")
# Delete the sandbox when the task is done sandbox.delete() return response.stdout
# Create and run 20 sandboxes concurrently from the same snapshotwith ThreadPoolExecutor(max_workers=20) as pool: results = list(pool.map(run_task, range(20)))print(results)| Parameter | Type | Description |
|---|---|---|
wait | int | Sets how many seconds create_from_snapshot waits for the sandbox to start. The default is 60. When wait runs out, create_from_snapshot returns the sandbox without raising. The returned sandbox may not have started. |
- Each create counts toward the organization’s concurrent sandbox limit when one is set.
- The SDK does not retry a create that the limit or a saturated admission queue refuses.
import asyncio
from daytona import AsyncDaytona
async def run_task(daytona: AsyncDaytona, shard: int) -> str: # Each task gets its own isolated sandbox from the same snapshot sandbox = await daytona.v2.create_from_snapshot("my-snapshot") response = await sandbox.process.run(f"python3 run.py --shard {shard}")
# Delete the sandbox when the task is done await sandbox.delete() return response.stdout
async def main() -> None: async with AsyncDaytona() as daytona: # Create and run 20 sandboxes concurrently from the same snapshot results = await asyncio.gather(*(run_task(daytona, i) for i in range(20))) print(results)
asyncio.run(main())| Parameter | Type | Description |
|---|---|---|
wait | int | Sets how many seconds create_from_snapshot waits for the sandbox to start. The default is 60. When wait runs out, create_from_snapshot returns the sandbox without raising. The returned sandbox may not have started. |
- Each create counts toward the organization’s concurrent sandbox limit when one is set.
- The SDK does not retry a create that the limit or a saturated admission queue refuses.
import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()
async function runTask(shard: number): Promise<string> { // Each task gets its own isolated sandbox from the same snapshot const sandbox = await daytona.v2.createFromSnapshot('my-snapshot') const response = await sandbox.process.run(`python3 run.py --shard ${shard}`)
// Delete the sandbox when the task is done await sandbox.delete() return response.stdout}
// Create and run 20 sandboxes concurrently from the same snapshotconst results = await Promise.all( Array.from({ length: 20 }, (_, i) => runTask(i)))console.log(results)| Parameter | Type | Description |
|---|---|---|
wait | number | Sets how many seconds createFromSnapshot waits for the sandbox to start. The default is 60. When wait runs out, createFromSnapshot returns the sandbox without throwing. The returned sandbox may not have started. |
- Each create counts toward the organization’s concurrent sandbox limit when one is set.
- The SDK does not retry a create that the limit or a saturated admission queue refuses.
# Create each sandbox from the same snapshot (run commands concurrently as needed)daytona sandbox create-from-snapshot my-snapshot# Create each sandbox from the same snapshot (issue requests concurrently as needed)curl 'https://api.daytona.com/sandboxes/from-snapshot' \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --data '{ "snapshot": "my-snapshot" }'Workload concurrency
Section titled “Workload concurrency”Workload concurrency runs multiple processes inside a single sandbox. A single sandbox can run an API server, a database, and background workers side by side: all processes share the sandbox’s filesystem and network, and exposed ports are reachable through previews.
Every command runs as a process: run starts a process and waits for it, while start returns a handle immediately and the process continues in the background. Sessions are shared shells: commands in a session run sequentially and share state, so parallelism comes from separate sessions or background processes. Pseudo terminal (PTY) sessions add interactive terminals.
from daytona import Daytona
daytona = Daytona()sandbox = daytona.v2.create()
# Start the server as a background process; the handle returns immediatelysandbox.process.start("python3 -m http.server 8000")
# Other commands run concurrently in the same sandboxresponse = sandbox.process.run(argv=[ "python3", "-c", "import time, urllib.request as u; time.sleep(1); print(u.urlopen('http://localhost:8000').status)",])print(response.stdout)import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()const sandbox = await daytona.v2.create()
// Start the server as a background process; the handle returns immediatelyawait sandbox.process.start('python3 -m http.server 8000')
// Other commands run concurrently in the same sandboxconst response = await sandbox.process.run({ argv: ['python3', '-c', "import time, urllib.request as u; time.sleep(1); print(u.urlopen('http://localhost:8000').status)"],})console.log(response.stdout)from daytona import Daytona
daytona = Daytona()sandbox = daytona.v2.create()
# start returns a handle immediately while the command runs in the backgroundhandle = sandbox.process.start("pip install numpy")
# Wait for completion and read the exit code laterresult = handle.wait()print(result.exit_code)import { Daytona } from '@daytona/sdk'
const daytona = new Daytona()const sandbox = await daytona.v2.create()
// start returns a handle immediately while the command runs in the backgroundconst handle = await sandbox.process.start('pip install numpy')
// Wait for completion and read the exit code laterconst result = await handle.wait()console.log(result.exitCode)Capacity and throughput
Section titled “Capacity and throughput”Fleet size operates within per-sandbox platform caps and your organization’s credit balance.
| Control | What it limits | Scope |
|---|---|---|
| Resources | Maximum vCPU, memory, and disk of a single sandbox | Sandbox |
| Credits | Spending across sandboxes; creates are rejected when the balance is exhausted | Organization |
Reclaiming capacity
Section titled “Reclaiming capacity”Delete sandboxes when their work is done to release compute and storage:
- Sandboxes release compute and storage immediately on delete.
- Spot sandboxes are deleted automatically when the platform needs capacity. See preemption.
Operating at scale
Section titled “Operating at scale”At high creation rates, two practices keep an application efficient:
- Make creates safe to retry with an idempotency key: a retried create with the same key returns the same sandbox instead of minting a new one.
- Wait with long-polls, not tight polling. The SDKs block on create until the sandbox runs. On the API,
waitlong-polls one sandbox, and the batch wait endpoint waits on up to200sandboxes over one connection, returning once one reaches the target state ortimeout_mselapses.
# Wait for one sandbox to reach running without tight pollingcurl 'https://api.daytona.com/sandboxes/{sandboxId}?wait=running' \ --header 'Authorization: Bearer YOUR_API_KEY'
# Wait on a whole batch over one connection (up to 200 IDs)curl 'https://api.daytona.com/sandboxes/batch-wait?ids={sandboxId1},{sandboxId2},{sandboxId3}&wait=running&timeout_ms=60000' \ --header 'Authorization: Bearer YOUR_API_KEY'