Ephemeral sandboxes
Ephemeral Sandboxes
An isolated machine with a lifetime. Run your code on it, and when the lifetime ends Maritime ends the machine and its disk. Each one is its own micro-VM on the sandbox engine, with its own kernel.
At a glance
- What it is
- An isolated Linux micro-VM on the sandbox engine with a lifetime from 5 minutes to 24 hours, ended with its disk when the lifetime ends. Its id is
sbx-.... - Use it for
- Code a model wrote, test runs, analysis of a file that must not stay on disk.
- Use something else when
- Files must outlive the run: Persistent Sandbox. Many at once: Swarm. A screen: Linux desktop.
- Start it
- New sandbox (
?type=ephemeral),POST /api/sandboxes/ephemeral,maritime sandboxes ephemeral create, orephemeral.createEphemeralin the SDK. - Lifecycle
- Starts, runs your commands through the Sandboxes API, and the sandbox engine ends it with its disk at
expiresAt.POST /api/sandboxes/ephemeral/{id}extends it. - Price
- $0.0045 per vCPU-hour + $0.00225 per GB-hour while running, per second (small, 2 vCPU and 4 GB, is $0.018 an hour). It never sleeps, so there is no disk line.
- Limits
- Lifetime 5 to 1,440 minutes. Sizes small (default), medium, large and xlarge, up to 16 vCPU and 32 GB. Counts toward awake at once while running.
- Related
- Persistent Sandboxes, Swarms, REST API.
When to use it
- Running code a model wrote, where nothing should outlive the run.
- Tests for a coding agent, one clean machine per run.
- Analysis of an uploaded file that must not stay on disk.
If the machine should keep its files between runs, use a Persistent Sandbox instead.
From the dashboard
Open New sandbox (/dashboard/new-sandbox?type=ephemeral), name it, pick a size and click Start. It gets the default lifetime, 60 minutes; for another lifetime, use the API below.
From the API
One call starts a sandbox with a lifetime: POST /api/sandboxes/ephemeral. When the lifetime ends, the sandbox engine ends it, disk included. The key needs the sandboxes scope (or manage).
# start it: ended, disk and all, 60 minutes from now
curl -X POST https://api.maritime.sh/api/sandboxes/ephemeral \
-H "Authorization: Bearer $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "job-42", "lifetimeMinutes": 60, "size": "small"}'
# 201: the Sandbox object plus its expiry
# {"id": "sbx-k3m7q2ab", "name": "job-42", "state": "running", "size": "small",
# "dataPlaneUrl": "https://api-sbx-k3m7q2ab.sbx.maritime.sh/v1", "secret": "...",
# ..., "expiresAt": "2026-10-26T18:00:00+00:00"}Every body field is optional. lifetimeMinutes is 5 to 1,440, default 60. The other fields are those of POST /v1/sandboxes: name, from (a stock template such as base or python, a saved snapshot, or an OCI image; default base), size (default small), screen, network, endUser, forEndUser, env, metadata and failFast. The idle timer fields idleTimeoutSeconds and keepAwake are refused. A create with the name of a live sandbox returns that sandbox with status 200. Send an Idempotency-Key header to retry safely.
The answer is the Sandbox object, the same as GET /v1/sandboxes/{id}, plus expiresAt. Its id (sbx-...) works with every /v1/sandboxes call and with the data plane. Refusals are the Sandboxes Error object (code, message, hint, docUrl, requestId).
To extend a sandbox, call POST /api/sandboxes/ephemeral/{id} with {"lifetimeMinutes": 60}. The lifetime restarts from that call. To end it now, call DELETE /api/sandboxes/ephemeral/{id}; calling it again is safe.
# 30 more minutes from now
curl -X POST https://api.maritime.sh/api/sandboxes/ephemeral/sbx-k3m7q2ab \
-H "Authorization: Bearer $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"lifetimeMinutes": 30}'
# end it now
curl -X DELETE https://api.maritime.sh/api/sandboxes/ephemeral/sbx-k3m7q2ab \
-H "Authorization: Bearer $MARITIME_API_KEY"List your live ephemeral sandboxes, newest first. The list also has sandboxes you made with /v1/sandboxes or the E2B-compatible API, but not swarm sandboxes. expiresAt is null for a sandbox that never ends by itself.
curl -H "Authorization: Bearer $MARITIME_API_KEY" \
https://api.maritime.sh/api/sandboxes/ephemeral
# {"sandboxes": [{"id": "sbx-k3m7q2ab", ..., "expiresAt": "..."}], "nextCursor": null}Commands and files
An ephemeral sandbox is a sandbox of the Sandboxes API, so commands and files run through it: the SDK's sandboxes client, maritime sandbox in the CLI, or the data plane at the sandbox's dataPlaneUrl.
import { Maritime } from 'maritime-sdk'
const maritime = new Maritime({ apiKey: process.env.MARITIME_API_KEY })
const box = await maritime.ephemeral.createEphemeral({ lifetimeMinutes: 30 })
const sbx = await maritime.sandboxes.connect(box)
const { stdout } = await sbx.run('uname -a')
console.log(stdout, 'ends at', box.expiresAt)
await maritime.ephemeral.killEphemeral(box.id) // or let the lifetime endimport os
from maritime import Maritime
client = Maritime(api_key=os.environ["MARITIME_API_KEY"])
box = client.ephemeral.create_ephemeral(lifetime_minutes=30)
sbx = client.sandboxes.connect(box["id"])
print(sbx.run("uname -a").stdout, "ends at", box["expiresAt"])
client.ephemeral.kill_ephemeral(box["id"]) # or let the lifetime endmaritime sandboxes ephemeral create --name job-42 --lifetime 30m
maritime sandbox exec sbx-k3m7q2ab -- uname -a
maritime sandbox cp ./data.csv sbx-k3m7q2ab:/tmp/data.csv
maritime sandboxes ephemeral kill sbx-k3m7q2abOver plain HTTP, send the command to {dataPlaneUrl}/commands with the sandbox's secret in X-Maritime-Token. The answer is the Command object, with exitCode, stdout and stderr, when the process exits.
curl -X POST "$DATA_PLANE_URL/commands" \
-H "X-Maritime-Token: $SANDBOX_SECRET" \
-H "Content-Type: application/json" \
-d '{"cmd": "uname -a"}'E2B-compatible API
Code written for the stock E2B SDKs (e2b and @e2b/code-interpreter) works against Maritime. Set E2B_DOMAIN to sbx.maritime.sh and E2B_API_KEY to a Maritime key with the sandboxes scope. The SDK then calls https://api.sbx.maritime.sh, which serves Maritime's /e2b routes; set E2B_API_URL only to point it somewhere else. A sandbox made this way is a Maritime sandbox, billed at the same rates.
export E2B_DOMAIN=sbx.maritime.sh
export E2B_API_KEY=$MARITIME_API_KEY
# your existing E2B code runs unchanged, for example:
python -c 'from e2b import Sandbox; print(Sandbox.create().commands.run("uname -a").stdout)'Lifetime
lifetimeMinutesis 5 to 1,440 (24 hours), default 60. Outside that range the call returns 400invalid_request.- The lifetime counts from the call. Extending it restarts it from now.
- The sandbox engine ends the sandbox, disk included, when the lifetime is over. No separate cleanup job is involved.
- You can end it earlier with
DELETE /api/sandboxes/ephemeral/{id}ormaritime sandboxes ephemeral kill <id>. - A wrong id, or another account's sandbox, returns 404
sandbox_not_found.
Sizes, limits and billing
| Size | vCPU | Memory | Disk | Per hour |
|---|---|---|---|---|
| small (default) | 2 | 4 GB | 12 GB | $0.018 |
| medium | 4 | 8 GB | 50 GB | $0.036 |
| large | 8 | 16 GB | 125 GB | $0.072 |
| xlarge | 16 | 32 GB | 250 GB | $0.144 |
- It is billed per second while running, with no minimum: $0.0045 per vCPU-hour plus $0.00225 per GB-hour of memory, before your plan's discount. It draws from your plan's usage credit and counts toward the free trial's $20. See Billing & plans.
- While it runs, it counts toward your plan's machines awake at once.
- Without a plan or the free trial, a create returns 402
plan_required, whosecheckoutUrlis the billing page. At the awake limit it returns 429concurrency_limit.
Every number is in Limits & errors.