Agent sandboxes

Give the agent its own computer

Run the code an agent writes in an isolated VM, keep each user's files between turns, and open the app it built over a short-lived credential.

sandboxes
volcano cloud sandboxes exec --preset python3.12 -- python -c 'print(6 * 7)'42volcano cloud sandboxes exec --preset node22 -- \  node -e 'console.log(process.version)'v22.22.0✓ each command ran in its own VM, cleaned up when it exited# keep a session when the next command needs the last one's files
Overview

The agent wrote the code. Something has to run it.

An agent that analyzes a spreadsheet, answers with a chart or fixes a failing test has to execute code nobody has read. Run it inside your function and it shares a process with your service key and your database connection. Start a fresh container for every command and the next turn loses the files, the packages and the server the last one left behind.

On Volcano that code runs in a Sandbox: an isolated VM your backend starts with a service key. Run a single command and the VM is terminated when it finishes, or keep a session per conversation so the files in /workspace and the processes the agent started are still there next turn. Suspend it between turns without paying for the time, open the server it started over a short-lived credential, and retry a command under the same request ID without running it twice.

Execution

The agent runs its code and reads what happened

Write the model's code into the session and run it. stdout, stderr and the exit code come back in one response, and a nonzero exit is a result rather than an error, so a traceback goes back to the model, which fixes the script and runs it again. For a self-contained calculation, skip the session: a one-shot command gets its own VM and is cleaned up when it exits.

  • python3.12 and node22 presets, or your own template
  • Each session is its own VM, apart from your functions
  • Driven from the API, CLI, SDKs, MCP or dashboard
analyst · one turn in session 7cdbb4e3
write  /workspace/sales.xlsx   the user's uploadexec   step_1.py               exit 1 · KeyError: 'Region'write  /workspace/step_2.py    the model fixed the column nameexec   step_2.py               exit 0 · saved revenue_by_region.pngread   /workspace/revenue_by_region.png✓ the reply quotes what the code printed, not a guess
Workspaces

Each user's files are still there next turn

Give each conversation a session instead of a new VM per command. The files in /workspace and the processes the agent started carry over between turns, so a follow-up reads the results the last turn saved instead of recomputing them. Suspend the session when the reply is sent and resume it on the next message: suspended time uses no runtime, and the snapshot behind it is included.

  • Suspend keeps processes and files; resume picks them up
  • Billed on memory × running time, not per command
  • Up to 1 hour per session on Hobby or 8 hours on Superagent
one session, two turns
volcano cloud sandboxes suspend $SESSION_ID# twenty minutes later, the user asks a follow-upvolcano cloud sandboxes resume $SESSION_IDvolcano cloud sandboxes get $SESSION_ID  "state": "running",volcano cloud sandboxes exec $SESSION_ID -- python /workspace/follow_up.pyread monthly.csv from the last turnfastest growth in Q3: west, +18.4%✓ files intact · the twenty minutes suspended used no runtime
Previews

Show the user the app the agent built

A coding agent starts its dev server in the session. Your backend checks that the user owns the thread, then asks for a credential bound to that session and that port, which expires within five minutes. The page posts it to set a secure, HTTP-only cookie and the app opens in the browser; once it expires, the page asks for a fresh one and reconnects. HTTP, WebSocket, SSE and gRPC all use the same URL.

  • Bound to one session and one port
  • Expires within five minutes, never in a query string
  • Grant a signed-in user one session, and only that one
the preview URL for port 3000
# four requests to the same URLno credential                        → refusedcredential issued for port 8080      → refusedcredential for 3000, in the header   → 200 · the app renderssame credential, six minutes later   → refusedthe credential decided, not the URL
Path to production

How it works

  1. 01

    Pick the environment

    Start from the python3.12 or node22 preset, or deploy a template from a Dockerfile fragment with the libraries your agent needs. Volcano builds and validates it before it goes live.

  2. 02

    A session per conversation

    Create it from your backend with a service key and keep its ID on the thread. Grant the signed-in user that session if their browser needs to reach it.

  3. 03

    Run each tool call

    Write the model's code to /workspace, run it under a request ID checkpointed in a durable step, and hand stdout, stderr and the exit code back to the model.

  4. 04

    Suspend between turns

    Suspend when the reply is sent and resume on the next message. Copy anything worth keeping to file storage before the session ends.

Code

A tool, a turn, a preview, and the deploy

Python
import json import os import shlex from volcano_sdk import ConflictError, VolcanoClient # Sandboxes take a service key, so these are project variables. The key stays # in this function; the code the model wrote runs in a VM that never sees it. client = VolcanoClient( anon_key=os.environ["VOLCANO_ANON_KEY"], service_key=os.environ["VOLCANO_SERVICE_KEY"], api_url=os.environ["VOLCANO_API_URL"], ) PROJECT_ID = os.environ["VOLCANO_PROJECT_ID"] def handler(event, context): # The agent sends the code the model wrote and one request_id per tool # call, and sends the same ID again if it has to retry the call. code = event["code"] request_id = event["request_id"] try: # A fresh VM for this one command, terminated once it exits. result = client.sandboxes.exec( PROJECT_ID, f"python -c {shlex.quote(code)}", region="us-east-1", preset="python3.12", request_id=request_id, ) except ConflictError: # Same ID, outcome unknown: Volcano refuses rather than run it twice. return {"statusCode": 409, "body": json.dumps({"error": "outcome unknown, not rerun"})} # A nonzero exit is a result, not an error. The traceback goes back to the # model so it can fix the script and call the tool again. return { "statusCode": 200, "body": json.dumps({ "stdout": result.stdout, "stderr": result.stderr, "exit_code": result.exit_code, }), }
Return

What you pay for, and what you don't

0.5 GBhfor 30 minutes at 1 GiB

Runtime is configured memory times running time, so a session that runs twenty commands costs the same as one that runs one.

0 GBhwhile a session is suspended

Suspended time uses no allowance or credits, and the snapshot that keeps its files and processes is included.

1 VMper session

Each hosted session is its own isolated VM, so there is no execution fleet of yours to run or to keep apart from your data.

What you get

Isolated by default

Code the model wrote runs in its own VM, never in your function.

Context that carries over

A follow-up reads the files the last turn left in /workspace.

No double runs

A command retried under the same request ID is never run a second time.

Platform

Built on Volcano

Sandboxes
  • Where the code runs

    Isolated VMs from a preset or your own template, as one-shot commands or sessions with files, processes and HTTP ports.

Durable functions
  • The agent loop

    Durable functions that checkpoint each model call, command and request ID, so a retry never runs the code twice.

Authentication
  • Who can reach a session

    Signed-in users your backend grants one session to, who can run commands and connect but never create or terminate one.

File storage
  • Files that outlive the session

    Buckets for the uploads the agent reads and the charts and reports it produces, kept after the session ends.

Frequently asked questions

Read the docs
How is the agent's code kept away from my app?

It runs in a Sandbox, not in your function. Each hosted session is its own isolated VM, so the code the model wrote never shares a process with your service key, your database connection or another user's session. Only a service key, a platform token or a full-scope project access token can create one, anonymous keys are refused, and a signed-in user reaches a session only after your backend grants it. Local Sandboxes run as Docker containers that share your machine's kernel, so use hosted ones when you need VM isolation.

Which languages and libraries can the code use?

Start from the python3.12 or node22 preset. When the agent needs more, deploy a custom template: a Dockerfile fragment using RUN, COPY and CMD, without FROM, on a base image Volcano supplies. Volcano builds and validates it before it activates, and sessions already running keep the image they started with. Deploy it from the CLI, the API, an SDK or the dashboard.

How long do a session's files last?

As long as the session. Files in /workspace and running processes survive suspend and resume, and are removed when the session is terminated or reaches its lifetime: up to 1 hour on Hobby or 8 hours on Superagent, counted from creation with suspended time included. Copy anything worth keeping, such as a chart or a report, to file storage before then. A suspended session keeps its capacity reserved, so terminate it when the conversation is over.

How does a user open the app the agent built?

Have the agent start the server in the session, then request a credential for that session and port from your backend. It expires within five minutes. Send it as the X-Volcano-Sandbox-Token header, or have the page post it in a form to the URL followed by _volcano/access, which sets a secure, HTTP-only cookie and redirects to the app. The start command can return before the server listens, so poll a health route first. To let the user's own client in, grant them the session instead: a grant lets one signed-in user run commands, use files and connect to that session, never create, suspend or terminate one.

What happens when a command is retried?

Send the same request ID and it is not run again. A command that finished returns its stored result. One whose outcome is unknown, because it is still running or its result was lost, is refused with a 409. A new ID means a new command, so create one per intent, and inside a durable function create it in a step so a replay reuses it.

How is it billed?

By configured memory times running time. A 1 GiB session running for 30 minutes uses 0.5 GBh however many commands it runs, idle time between them included. Suspended time is free and snapshots are included. Hobby includes 5 GBh per monthly cycle, up to 1 GiB per session and 5 sessions at a time, and refuses new starts and resumes once the allowance is used. Superagent includes 20 GBh, then 25 credits per 5 GBh block, with up to 2 GiB per session and no plan limit on concurrency beyond a 100-session safety ceiling. Local sessions are not billed.

Ready to give your agent a sandbox?

Build, deploy, and scale on Volcano's global platform — free to start, with no infrastructure to manage.

Explore more solutions