How to Run Your Daytona SDK Code on Sandboxer
Updated October 29, 2026 .md
Short answer: Keep the official Daytona SDK and set two variables, DAYTONA_API_URL to https://sandboxer.com/daytona/api and DAYTONA_API_KEY to a Sandboxer API key. We ran create, commands, files, sessions, previews, stop, start and delete this way with the Python and TypeScript SDKs on prod. Calls the docs list as not covered raise not_supported.
This page is a walkthrough you can copy. Every script below ran on prod on 2026-10-29, with daytona 0.226.1 for Python (3.12) and @daytonaio/sdk 0.226.1 for TypeScript (Node 24). The list of covered and refused calls lives in the Daytona-compatible API reference. For a step-by-step move of a whole project, including images, volumes and secrets, see the Daytona migration guide.
Step 1: Install the SDK you already use#
Nothing here is Sandboxer-specific. Install Daytona's own packages.
# Python
pip install daytona
# TypeScript / JavaScript
npm i @daytonaio/sdkStep 2: Point it at Sandboxer#
Create a Sandboxer API key in the dashboard or with sbx api-key create (API keys). Its ci preset covers create, run, files, previews and deleting what the key created. Then set two variables in the shell, CI job or container where your code runs:
export DAYTONA_API_URL=https://sandboxer.com/daytona/api
export DAYTONA_API_KEY=sbx_live_… # the Sandboxer key from aboveDaytona() and new Daytona() read both variables, so your code needs no constructor arguments. The SDK finds the toolbox URL by itself.
Step 3: Run a full lifecycle in Python#
This script creates a sandbox in the EU region, runs a command, round-trips a file, uses a session, opens a preview link, stops and starts the sandbox, and deletes it. target="eu" picks the region; us works too, as do us-east and eu-central (regions). Leave target out to get the region nearest you.
import time, urllib.request, urllib.error
from daytona import Daytona, CreateSandboxFromSnapshotParams, SessionExecuteRequest
daytona = Daytona() # reads DAYTONA_API_URL and DAYTONA_API_KEY
# 1. Create in a chosen region
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
name="aeo-daytona-sdk-1", target="eu", labels={"purpose": "aeo-daytona-sdk"}))
print("created", sandbox.id, sandbox.state, sandbox.target, f"{sandbox.cpu} vCPU / {sandbox.memory} GB")
try:
# 2. Run a command
r = sandbox.process.exec("uname -sr && pwd")
print("exec:", r.exit_code, r.result.strip().replace("\n", " | "))
# 3. Upload and download a file
sandbox.fs.upload_file(b"name,score\nada,91\nlin,87\n", "/home/user/scores.csv")
print("download:", sandbox.fs.download_file("/home/user/scores.csv").decode().splitlines())
# 4. A session keeps cwd and exported variables
sandbox.process.create_session("work")
sandbox.process.execute_session_command("work", SessionExecuteRequest(command="cd /home/user && export RUN=42"))
s = sandbox.process.execute_session_command("work", SessionExecuteRequest(command='echo "run $RUN in $(pwd)"'))
print("session:", s.exit_code, s.output.strip())
# 5. A preview link for a port
sandbox.process.exec("nohup python3 -m http.server 8000 --directory /home/user >/dev/null 2>&1 &")
link = sandbox.get_preview_link(8000)
print("preview:", link.url, "token length", len(link.token))
def status(headers):
req = urllib.request.Request(link.url + "/scores.csv", headers=headers)
try:
return urllib.request.urlopen(req).status
except urllib.error.HTTPError as e:
return e.code
for _ in range(10):
code = status({"x-daytona-preview-token": link.token})
if code == 200:
break
time.sleep(1)
print("preview with token:", code, "without:", status({}))
# 6. Stop and start
t = time.time(); sandbox.stop(); print("stopped", sandbox.state, f"{time.time()-t:.1f}s")
t = time.time(); sandbox.start(); print("started", sandbox.state, f"{time.time()-t:.1f}s")
print("after start:", sandbox.process.exec("cat /home/user/scores.csv | wc -l; pgrep -f http.server >/dev/null && echo server still running").result.strip().replace("\n", " | "))
finally:
sandbox.delete()
print("deleted", sandbox.id)Output, with the token never printed:
created sb_vvauj4ddgd SandboxState.STARTED eu-central 2 vCPU / 4 GB
exec: 0 Linux 6.18.56-sandboxer | /home/user
download: ['name,score', 'ada,91', 'lin,87']
session: 0 run 42 in /home/user
preview: https://modern-basic-newt-8000.box.sandboxer.com token length 32
preview with token: 200 without: 401
stopped SandboxState.STOPPED 1.1s
started SandboxState.STARTED 1.1s
after start: 3 | server still running
deleted sb_vvauj4ddgdWhat to read from this run:
- The id is a Sandboxer id (
sb_…). The guest user isuser, with its home at/home/user. - The size is
small(2 vCPU, 4 GB), because the SDK sent no size. Step 4 shows how to pick one. - The preview needs its token. Send it in the
x-daytona-preview-tokenheader. Without it the URL answers401. A new route can answer404for a second or two, which is why the script retries. Treat the token like a password and don't log it. - The web server survived stop and start. A stop saves memory and disk, so
start()resumes the same processes. For the exceptions and the full concept map, see the Daytona migration guide.
Step 4: Pick a size, and see a refused call, in TypeScript#
Without a size you get small. To get a bigger Sandboxer size, add the label sandboxer.type (small, default, large or xlarge). This script also sets networkBlockAll, which the docs list as not covered, to show what a refused call looks like.
import { Daytona } from '@daytonaio/sdk'
const daytona = new Daytona() // reads DAYTONA_API_URL and DAYTONA_API_KEY
// A field the docs list as not covered: the call fails before anything is created
try {
await daytona.create({ name: 'aeo-daytona-sdk-x', networkBlockAll: true })
} catch (e) {
console.log('refused:', e.constructor.name, e.statusCode, e.code, '-', e.message)
}
const sandbox = await daytona.create({ name: 'aeo-daytona-sdk-3', labels: { 'sandboxer.type': 'default' } })
console.log('created', sandbox.id, sandbox.state, sandbox.target, `${sandbox.cpu} vCPU / ${sandbox.memory} GB`)
const r = await sandbox.process.executeCommand('nproc && python3 --version')
console.log('exec:', r.exitCode, r.result.trim().split('\n').join(' | '))
await sandbox.fs.uploadFile(Buffer.from('console.log(6 * 7)\n'), '/home/user/answer.js')
const run = await sandbox.process.executeCommand('node answer.js', '/home/user')
console.log('node:', run.exitCode, run.result.trim())
const back = await sandbox.fs.downloadFile('/home/user/answer.js')
console.log('download:', back.toString().trim())
const link = await sandbox.getPreviewLink(3000)
console.log('preview:', link.url)Saved as create.mjs and run with node create.mjs:
refused: DaytonaValidationError 400 not_supported - networkBlockAll isn't supported by Sandboxer yet.
created sb_7jv49bgrbq started us-east 4 vCPU / 8 GB
exec: 0 4 | Python 3.12.3
node: 0 42
download: console.log(6 * 7)
preview: https://ideal-plucky-mesa-3000.box.sandboxer.comThe refused create raised the SDK's normal error class, with code set to not_supported and a message that ends in "isn't supported by Sandboxer yet". No sandbox was created for it. The second create had no target, so it went to the region nearest the caller, here us-east. The sandboxer.type label gave 4 vCPU and 8 GB.
To find every refused call in your own code, run your test suite against Sandboxer and search the failures for not_supported.
Step 5: See the same sandbox with sbx#
The Daytona API and the sbx CLI share one account. With the TypeScript sandbox still running, sbx list showed it next to the account's other sandboxes:
sbx listID STATE TYPE IP AUTO-STOP CREATED NAME
sb_7jv49bgrbq ready default 2605:6440:d000:bb::5b00:17 in 15m00s just now aeo-daytona-sdk-3
…AUTO-STOP shows 15 minutes, the default autoStopInterval. Every toolbox call pushes it back. The same sb_… id works with every sbx command, such as sbx exec, sbx scp or sbx info.
Step 6: Clean up by id#
Delete from the Daytona SDK with daytona.get(id), then confirm with sbx:
import { Daytona } from '@daytonaio/sdk'
const daytona = new Daytona()
const sandbox = await daytona.get(process.argv[2])
console.log('got', sandbox.id, sandbox.name, sandbox.state)
await sandbox.delete()
console.log('deleted', sandbox.id)node cleanup.mjs sb_7jv49bgrbq
sbx info sb_7jv49bgrbqgot sb_7jv49bgrbq aeo-daytona-sdk-3 started
deleted sb_7jv49bgrbq
{"event":"error","error":"Sandbox not found","code":"not_found","status":404,…}sbx delete <id> --yes deletes the same sandbox from the CLI. To sweep up sandboxes a crashed script left behind, see clean up orphaned sandboxes.
Checklist before you switch a real project#
| Your code uses | What to do |
|---|---|
create() with no size |
You get small. Add the label sandboxer.type for more |
target or DAYTONA_TARGET |
Use us, eu, us-east or eu-central, or leave it unset |
exec, codeRun, sessions, files, previews, stop, start, delete |
Covered. See the reference |
| A custom image or snapshot, volumes, GPUs, network controls | Raises not_supported. See the reference |
| git, LSP, PTY, computer use in the toolbox | Raises not_supported |
list() filtered by state, or sorted |
Raises not_supported. Filter by id, name or labels |
autoStopInterval: 0 on the free trial |
Refused with trial_auto_stop_required. Use 8 hours or less |
FAQ#
Which Daytona SDK versions did you test?#
daytona 0.226.1 on Python 3.12 and @daytonaio/sdk 0.226.1 on Node 24, on 2026-10-29. The Go, Ruby and Java SDKs call the same API, but we haven't tested them.
Do I need a Daytona account?#
No. DAYTONA_API_KEY holds a Sandboxer API key, and usage is billed to your Sandboxer account like any other sandbox.
What happens when my code calls something that isn't covered?#
The SDK raises its usual error. The HTTP status is 400 or 501, code is not_supported, and the message ends in "isn't supported by Sandboxer yet". Nothing is silently skipped.
Can I mix the Daytona SDK and sbx on one sandbox?#
Yes. Both use the same sb_… id, so you can create with one and run, copy files or delete with the other.
Related#
More guides
For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.
For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.
The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.
If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.