Sandbox lifecycle
Create a sandbox, reconnect to it from somewhere else, find it again in a list, and kill it when you are done.
Create a sandbox
Sandbox.create() starts a new sandbox from a template and resolves once the sandbox has been created. The default template is base. Both SDKs read your key from IMPELLO_API_KEY.
import { Sandbox } from '@impello/sdk'
const sandbox = await Sandbox.create('base', {
timeoutMs: 600_000,
metadata: { run: 'nightly-42' },
envs: { NODE_ENV: 'production' },
})
console.log(sandbox.sandboxId)The timeout is in milliseconds in TypeScript and in seconds in Python. A sandbox that reaches its timeout is killed unless you set lifecycle. See Timeouts.
Every running sandbox counts against your plan's concurrency limit: 3 at once on Micro, 10 on Base, 100 on Scale. Paused sandboxes do not count, and there is no limit on how many you keep paused. See Limits and pricing.
Options
| TypeScript | Python | Default | What it does |
|---|---|---|---|
template | template | 'base' | Template name or ID. See Templates. |
timeoutMs | timeout | 300_000 / 300 | How long the sandbox lives. The maximum depends on your plan; see Limits and pricing. |
metadata | metadata | {} | String key-value pairs stored with the sandbox. Filterable in list. |
envs | envs | {} | Environment variables set for every command run in the sandbox. |
secure | secure | true | Require a per-sandbox access token on every command, file and terminal call (the sandbox's control channel). See Secured sandboxes. |
allowInternetAccess | allow_internet_access | true | Outbound internet. false is the same as denying all egress in network. |
network | network | — | Egress rules and public traffic. See Network and public URLs. |
lifecycle | lifecycle | onTimeout: 'kill' | Pause instead of kill on timeout. See Timeouts. |
Metadata and environment variables
Metadata is for you: tag a sandbox with the user, job or run it belongs to, then find it again with list. Keys and values are strings. Anyone with one of your team's API keys can read them through getInfo and list.
Environment variables are for the sandbox: every command you run through the SDK sees them. They are ordinary process environment, not a secret store. A command can override them with its own envs.
import { Sandbox } from '@impello/sdk'
const sandbox = await Sandbox.create({
metadata: { userId: 'u_123', job: 'index' },
envs: { LOG_LEVEL: 'info' },
})
const a = await sandbox.commands.run('echo $LOG_LEVEL')
console.log(a.stdout) // info
const b = await sandbox.commands.run('echo $LOG_LEVEL', {
envs: { LOG_LEVEL: 'debug' },
})
console.log(b.stdout) // debugSecured sandboxes
A sandbox is created with secure: true unless you say otherwise. The SDK receives a token scoped to that one sandbox when it creates or connects to it, and sends the token with every command, file and terminal request. Upload and download URLs from uploadUrl() and downloadUrl() (upload_url() and download_url() in Python) are signed with the same token, and only a secured sandbox accepts a signature expiry. secure covers the control channel only — a server you start on a port is still reachable by anyone who knows the sandbox ID unless you also set network.allowPublicTraffic: false. See Network and public URLs.
Set secure: false only when something other than the SDK has to call the sandbox directly and cannot carry the token. Such a sandbox has no access token, and its file URLs are unsigned.
Connect to a running sandbox
Sandbox.connect(sandboxId) returns a client for a sandbox that already exists. Use it to reach the same sandbox from another process, a queue worker or a serverless function. If the sandbox is paused, connect resumes it. In the snippets from here on, sandboxId (sandbox_id in Python) is the ID of a sandbox you already have, and sandbox is the client the previous snippet created.
import { Sandbox } from '@impello/sdk'
const sandbox = await Sandbox.connect(sandboxId, { timeoutMs: 600_000 })
const result = await sandbox.commands.run('uptime')
console.log(result.stdout)The sandbox must be running or paused. Otherwise connect throws SandboxNotFoundError (SandboxNotFoundException in Python). See Errors.
An instance you already hold has the same method: await sandbox.connect() in TypeScript and sandbox.connect() in Python. It is the shortest way to resume a sandbox you paused a moment ago. See Pause, resume and snapshots.
Get sandbox information
getInfo() (get_info() in Python) fetches the current record for a sandbox: its state, template, metadata and when it will time out.
import { Sandbox } from '@impello/sdk'
const info = await sandbox.getInfo()
console.log(info.state, info.endAt, info.metadata)
// Or by ID, without a client
const same = await Sandbox.getInfo(sandboxId)Common fields
| TypeScript | Python | Meaning |
|---|---|---|
sandboxId | sandbox_id | The sandbox ID. |
templateId | template_id | ID of the template it was created from. |
name | name | Template name, when the template has one. |
metadata | metadata | The metadata you set at create. |
startedAt | started_at | When the sandbox started. |
endAt | end_at | When the current timeout expires. |
state | state | running or paused. |
cpuCount | cpu_count | vCPUs. |
memoryMB | memory_mb | Memory in MiB. |
allowInternetAccess | allow_internet_access | Whether outbound internet was allowed at create. |
network | network | The egress and public traffic settings. |
lifecycle | lifecycle | What happens on timeout, and whether it auto-resumes. |
sandboxDomain | sandbox_domain | Host the sandbox is served from. |
isRunning() (is_running() in Python) asks the sandbox itself rather than the control API. It returns false when the sandbox is no longer answering and raises on any other transport or auth failure.
List sandboxes
Sandbox.list() returns a paginator over your team's sandboxes. With no filter it includes both running and paused sandboxes, 100 per page; 100 is also the largest page the API will return. Pages come back from nextItems() (next_items() in Python) while hasNext (has_next) is true.
import { Sandbox } from '@impello/sdk'
const paginator = Sandbox.list({
query: {
metadata: { userId: 'u_123', job: 'index' },
state: ['running'],
},
limit: 50,
})
while (paginator.hasNext) {
const page = await paginator.nextItems()
for (const s of page) {
console.log(s.sandboxId, s.state, s.endAt)
}
}Metadata filters are combined with AND: a sandbox must match every pair. state takes any of running and paused. Listed entries carry the same fields as getInfo minus network, lifecycle, allowInternetAccess and sandboxDomain; call getInfo on one entry when you need those.
The dashboard shows the same list at dashboard.impello.ai/sandboxes.
Kill a sandbox
kill() stops the sandbox and frees it. It returns true when the sandbox was killed and false when no such sandbox exists, so killing twice is safe.
import { Sandbox } from '@impello/sdk'
await sandbox.kill()
// Or by ID, without a client
await Sandbox.kill(sandboxId)In Python, Sandbox is a context manager that kills the sandbox on exit:
from impello import Sandbox
with Sandbox.create() as sandbox:
print(sandbox.commands.run("echo hello").stdout)
# killed here
Async Python
Every call on this page has an AsyncSandbox form with the same name, awaited: await AsyncSandbox.create(), await AsyncSandbox.connect(sandbox_id), await sandbox.get_info(), await sandbox.kill(). AsyncSandbox.list() returns an AsyncSandboxPaginator whose next_items() is awaited, and the context manager is async with await AsyncSandbox.create() as sandbox:.
Next: Run commands.