Skip to navigation

Deploy a sandbox preview

A sandbox deployment runs your code in a container and exposes one port at a public HTTPS preview URL. Use it to check a running server, such as a demo API, a mock server for a simulation, or a web app that a coding agent built. This is the session tier: the preview URL lives as long as its container, at most until the retention deadline. It is not durable hosting. To share a static HTML page, use the builtin:publish_page tool instead.

You can deploy from three places. Each one calls the same Runtype Sandbox endpoints:

Where you deploy fromDeployDestroy
Runtype MCP serverdeploy_sandbox tooldestroy_sandbox tool
TypeScript SDKclient.tools.deployCfSandbox()client.tools.cleanupCfSandbox()
Runtype APIPOST /v1/tools/sandbox/cf-sandbox/deployDELETE /v1/tools/sandbox/cf-sandbox/{sandboxId}

Before you begin

Make sure that you have the following:

  • A Runtype API key with the TOOLS:EXECUTE permission, in the RUNTYPE_API_KEY environment variable. For more information, see Managing API keys.
  • A server that listens on a port other than 3000. Runtype reserves port 3000 and port 2468. If you omit port, Runtype exposes port 8080.

Deploy code

Send your code in the code field. Runtype writes it to main.js, main.ts, or main.py, based on language, and then starts it.

The following example deploys a Node.js server that listens on port 8080:

deploy.ts
import { RuntypeClient } from '@runtypelabs/sdk'
const client = new RuntypeClient({
apiKey: process.env.RUNTYPE_API_KEY,
})
const code = `
const http = require('http')
http
.createServer((req, res) => res.end('Hello from a sandbox'))
.listen(8080)
`
const result = await client.tools.deployCfSandbox({
code,
language: 'javascript',
port: 8080,
})
console.log(result.sandboxId, result.previewUrl)

A successful deploy returns a response similar to the following:

{
"sandboxId": "2f1c9b7e-5d3a-4e8f-9a61-0c7d2b4e8f13",
"previewUrl": "YOUR_PREVIEW_URL",
"output": "",
"status": "running",
"stage": "expose_port",
"effectivePolicy": {
"retention": "unlimited",
"origin": "legacy-default",
"expiresAt": null,
"metering": "legacy-untracked",
"containerEpoch": 1,
"freshContainer": true,
"previewUrlChanged": false
}
}

Open previewUrl in a browser to see the running server. Keep sandboxId. You need it to redeploy and to destroy the sandbox.

The preview URL is a subdomain URL that carries a token for the exposed port, so it reaches only that port. It requires no sign-in, so anyone who has the URL can open the server. Share it only with people who can see what the server returns.

The token is bound to the container. When a container stops and a later deploy starts a new one, the preview URL changes. The response reports this in effectivePolicy:

FieldDescription
containerEpochThe container the preview URL belongs to. A new number means a new container and a new URL.
freshContainertrue when this deploy started a new container. false when it reused the running one.
previewUrlChangedtrue when the preview URL differs from the one that the previous successful deploy to this sandbox returned, for example after a container restart or a port change. Update every place where you saved the old URL. false for a first deploy.

Don’t save a preview URL as an MCP server URL, an OAuth audience, or an external tool URL that must work for more than a day.

The following fields shape the deployment:

FieldTypeDescription
codestringRequired. The contents of the main file.
languagestringjavascript, typescript, or python. Defaults to typescript.
portnumberThe port that your server listens on. Defaults to 8080.
packageJsonstringThe contents of package.json. Runtype runs npm install before it starts your code. Ignored for Python.
filesobjectExtra files, as relative paths mapped to string contents. Paths can’t start with / or contain ...
startCommandstringReplaces the default start command: node main.js, npx tsx main.ts, or python3 main.py.
sandboxIdstringThe ID from an earlier deploy. Runtype deploys into that container instead of creating one.
retentionstring10m, 1h, 24h, or unlimited. Runtype destroys the sandbox when retention ends. Requires lifecycle policy.
sleepPolicyobject{ "mode": "idle", "after": "5m" } (also 15m, 30m, 1h), or { "mode": "never" } to keep the container running. Requires lifecycle policy.

retention and sleepPolicy require lifecycle policy for your organization. Without it, a request that sets either one fails with 403 and Explicit sandbox lifecycle policy is not enabled for this organization.

Redeploy changes

To update the code in a sandbox, deploy again and pass the sandboxId from the first response. Runtype writes your files into the same container, stops the previous server and every process that it started, and then runs the start command again. If another process in the sandbox still answers on port, the deploy fails at the start_process stage instead of starting a server that can’t bind the port.

The deploy waits up to 30 seconds for the new server to answer on port. If the server exits first, or doesn’t answer in time, the deploy fails at the wait_for_port stage and output includes the server’s logs.

If the container was still running, the preview URL stays the same. If it had stopped, the redeploy starts a new container and returns a new URL, and effectivePolicy.previewUrlChanged is true.

How long a sandbox stays up

If you omit retention, a Runtype Sandbox deploy has no Runtype expiry. The response reports this as "retention": "unlimited" with "expiresAt": null. The container itself doesn’t run forever:

  • When the preview receives no requests for a period of time, the container sleeps and your server stops. The default is 10 minutes. Set sleepPolicy to { "mode": "idle", "after": "1h" } to wait longer.
  • The container can also stop when your server exits or crashes.

A stopped server doesn’t restart on its own, and the preview URL stops responding. To bring it back, deploy again with the same sandboxId. The new container has a new preview URL.

To keep a server reachable at one URL for a bounded run, such as a simulation or an eval benchmark, set retention to 10m, 1h, or 24h and sleepPolicy to { "mode": "never" }. The container keeps running without requests until the retention deadline in effectivePolicy.expiresAt. From that deadline on, the preview URL returns 404, the same answer as an unknown token, and Runtype destroys the sandbox. Each successful redeploy starts a new retention window. A redeploy that fails keeps the previous retention window and sleepPolicy, for the sandbox and for billing. A request that sets { "mode": "never" } with unlimited or no retention fails with 400.

A kept-alive sandbox is billed for the whole time that it runs. A crash still stops the server and changes the URL on the next deploy. For a server that must stay up for more than a day, survive a crash, or keep its URL, use a managed deployment instead.

Destroy a sandbox

Destroy the sandbox when you no longer need the preview. Destroying stops and removes the container, and you can’t recover it.

destroy.ts
await client.tools.cleanupCfSandbox('YOUR_SANDBOX_ID')

Replace YOUR_SANDBOX_ID with the sandboxId from the deploy response. A successful call returns { "success": true }.

Deploy to a Daytona sandbox

The TypeScript SDK and the API can also deploy to a Daytona sandbox with client.tools.deploySandbox() or POST /v1/tools/sandbox/deploy. The request takes the same fields as a Runtype Sandbox deploy. Runtype uses your Daytona key if you saved one in Settings > Integrations, and a Runtype platform key otherwise. For more information, see Integrations (PostHog, Weaviate, Daytona).

A Daytona deploy differs from a Runtype Sandbox deploy in these ways:

  • Runtype destroys a Daytona sandbox after its retention window ends, 10 minutes after the last deploy. Each redeploy with the same sandboxId starts a new 10-minute window. The response shows the deadline in effectivePolicy.expiresAt.
  • After Runtype destroys the sandbox, a redeploy with its sandboxId returns 404 and Sandbox not found. Deploy without sandboxId to get a new one.
  • To destroy a Daytona sandbox early, call client.tools.cleanupSandbox() or DELETE /v1/tools/sandbox/{sandboxId}.

Troubleshoot a deploy

A deploy that fails inside the container still returns HTTP 200. The response has "status": "error", an error message, output with the logs so far, and stage set to the step that failed. The following table lists the stages and their common causes:

StageCommon cause
validate_portport is 3000 or 2468. Pick another port, such as 8080.
validate_filesA files path is absolute or contains ...
npm_installA dependency in packageJson failed to install. Read output.
start_processThe start command failed to launch, the previous server didn’t stop, or another process uses port. Read error.
wait_for_portThe server exited, or didn’t answer on port within 30 seconds. Read output.
expose_portRuntype couldn’t expose the port. Check that your server listens on port.

The API rejects some requests before it deploys anything:

  • 403 with The account sandbox spend limit has been reached: your account reached its spend limit. For more information, see Billing and plans.
  • 403 with Explicit sandbox lifecycle policy is not enabled for this organization: remove retention and sleepPolicy from the request.
  • 400 with requires finite retention (10m, 1h or 24h): sleepPolicy is { "mode": "never" }, but retention is unlimited or missing. Set retention to 10m, 1h, or 24h.

If the deploy succeeds but the preview URL doesn’t respond, the server might have crashed after it started, or the container might be asleep. Deploy again with the same sandboxId, and read output for errors.

Next steps