Skip to content

Sandbox Providers

Runra Runtime abstracts sandbox infrastructure behind a pluggable provider interface. Choose hosted, self-hosted, or local providers — your agent code stays the same.

ProviderKeyUse Case
Runra Sandboxrunra-sandboxHosted VM-backed sandboxes (production)
CubeSandboxcubesandboxSelf-hosted VM-backed sandboxes
DockerdockerLocal Docker containers (development)
E2Be2bE2B sandbox infrastructure

The recommended provider for production. Fully managed, VM-backed isolation, no infrastructure to maintain.

import { Runra } from "@runra/runtime";
const runra = new Runra({
sandbox: {
provider: "runra-sandbox",
apiKey: process.env.RUNRA_API_KEY,
apiUrl: "https://api.box.runra.dev", // optional, default
defaults: {
image: "node:22",
resources: { cpu: 2, memoryMb: 4096, diskMb: 10240 },
timeoutMs: 300_000,
idleTimeoutMs: 120_000,
},
},
// ... agent and observability config
});
ImageDescription
node:22Node.js 22 with npm, yarn, pnpm
node:20Node.js 20 LTS
python:3.12Python 3.12 with pip, uv
python:3.11Python 3.11
ubuntu:24.04Bare Ubuntu 24.04
ubuntu:22.04Bare Ubuntu 22.04
customBYO image (contact support)
  • VM-level isolation per sandbox
  • Pause & resume preserves full filesystem state
  • Configurable network policies
  • Automatic cleanup via timeoutMs and idleTimeoutMs
  • Up to 8 vCPUs and 16 GB RAM per sandbox
  • Expose ports for previews and dev servers
  • Full API reference

Run sandboxes on your own infrastructure using the open-source CubeSandbox engine. Ideal for air-gapped environments or compliance requirements.

const runra = new Runra({
sandbox: {
provider: "cubesandbox",
config: {
masterUrl: "https://cube-master.internal:3000",
masterToken: process.env.CUBE_MASTER_TOKEN,
templates: {
node: "template-node-22",
python: "template-python-312",
},
nodeSelector: {
zone: "us-east-1",
},
},
defaults: {
image: "node:22",
resources: { cpu: 2, memoryMb: 4096 },
},
},
});

Self-hosting CubeSandbox requires:

  1. A CubeMaster control plane
  2. One or more Cubelet compute nodes
  3. Image templates built on each node

See the CubeSandbox repository for setup instructions.

Use local Docker containers for development and testing. No API key required.

const runra = new Runra({
sandbox: {
provider: "docker",
config: {
socketPath: "/var/run/docker.sock", // default
// Or remote Docker:
// host: "tcp://192.168.1.100:2375",
images: {
node: "node:22-alpine",
python: "python:3.12-slim",
ubuntu: "ubuntu:24.04",
},
networkMode: "bridge", // "bridge" or "host"
autoRemove: true, // Clean up containers
},
defaults: {
image: "node:22",
resources: { cpu: 2, memoryMb: 2048 },
},
},
});

Docker provider maps Runra resources to Docker limits:

RunraDocker
cpu: 2--cpus=2
memoryMb: 4096--memory=4096m
diskMb: 10240--storage-opt size=10G
timeoutMs: 300000--stop-timeout
  • No VM-level isolation (shared kernel)
  • No pause/resume (simulated via docker pause which freezes processes)
  • File system not preserved on terminate
  • Suitable for development only, not production

Use E2B’s sandbox infrastructure:

const runra = new Runra({
sandbox: {
provider: "e2b",
config: {
apiKey: process.env.E2B_API_KEY,
domain: "e2b.dev", // optional
template: "base",
},
defaults: {
image: "node:22",
timeoutMs: 300_000,
},
},
});
interface SandboxConfig {
/** Provider key */
provider: "runra-sandbox" | "cubesandbox" | "docker" | "e2b" | "custom";
/** API key for hosted providers */
apiKey?: string;
/** Provider-specific configuration */
config?: Record<string, unknown>;
/** Default sandbox settings applied to all creates */
defaults?: {
image?: string;
resources?: {
cpu?: number;
memoryMb?: number;
diskMb?: number;
};
timeoutMs?: number;
idleTimeoutMs?: number;
env?: Record<string, string>;
workdir?: string;
network?: {
egress?: {
allow?: string[];
default?: "allow" | "deny";
};
};
};
}

Implement the SandboxProvider interface to integrate any sandbox infrastructure:

import type { SandboxProvider, SandboxHandle, SandboxConfig, ExecuteOptions, ExecuteResult } from "@runra/runtime";
interface MyProviderConfig {
clusterUrl: string;
authToken: string;
namespace: string;
}
class MySandboxProvider implements SandboxProvider {
readonly id = "my-provider";
private config!: MyProviderConfig;
async initialize(config: Record<string, unknown>): Promise<void> {
this.config = config as unknown as MyProviderConfig;
}
async create(config: SandboxConfig): Promise<SandboxHandle> {
const response = await fetch(`${this.config.clusterUrl}/api/v1/sandboxes`, {
method: "POST",
headers: {
"Authorization": `Bearer ${this.config.authToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
image: config.image,
cpu: config.resources?.cpu ?? 2,
memoryMb: config.resources?.memoryMb ?? 4096,
namespace: this.config.namespace,
}),
});
const data = await response.json();
return {
id: data.id,
state: "running",
image: config.image,
resources: config.resources || { cpu: 2, memoryMb: 4096 },
createdAt: data.createdAt,
};
}
async get(id: string): Promise<SandboxHandle> {
const response = await fetch(
`${this.config.clusterUrl}/api/v1/sandboxes/${id}`,
{ headers: { Authorization: `Bearer ${this.config.authToken}` } }
);
const data = await response.json();
return { id: data.id, state: data.state, image: data.image, createdAt: data.createdAt };
}
async list(opts?: { state?: string; limit?: number }): Promise<SandboxHandle[]> {
const url = new URL(`${this.config.clusterUrl}/api/v1/sandboxes`);
if (opts?.state) url.searchParams.set("state", opts.state);
if (opts?.limit) url.searchParams.set("limit", String(opts.limit));
const response = await fetch(url, {
headers: { Authorization: `Bearer ${this.config.authToken}` },
});
const data = await response.json();
return data.items.map((item: any) => ({
id: item.id,
state: item.state,
image: item.image,
createdAt: item.createdAt,
}));
}
async execute(id: string, command: string, opts?: ExecuteOptions): Promise<ExecuteResult> {
const response = await fetch(
`${this.config.clusterUrl}/api/v1/sandboxes/${id}/exec`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${this.config.authToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
command,
cwd: opts?.cwd,
env: opts?.env,
timeoutMs: opts?.timeoutMs,
}),
}
);
const data = await response.json();
return {
stdout: data.stdout,
stderr: data.stderr,
exitCode: data.exitCode,
durationMs: data.durationMs,
signal: data.signal ?? null,
};
}
async pause(id: string): Promise<void> {
await fetch(
`${this.config.clusterUrl}/api/v1/sandboxes/${id}/pause`,
{
method: "POST",
headers: { Authorization: `Bearer ${this.config.authToken}` },
}
);
}
async resume(id: string): Promise<void> {
await fetch(
`${this.config.clusterUrl}/api/v1/sandboxes/${id}/resume`,
{
method: "POST",
headers: { Authorization: `Bearer ${this.config.authToken}` },
}
);
}
async terminate(id: string): Promise<void> {
await fetch(
`${this.config.clusterUrl}/api/v1/sandboxes/${id}`,
{
method: "DELETE",
headers: { Authorization: `Bearer ${this.config.authToken}` },
}
);
}
async writeFile(id: string, path: string, content: string): Promise<void> {
await fetch(
`${this.config.clusterUrl}/api/v1/sandboxes/${id}/files`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${this.config.authToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ path, content }),
}
);
}
async readFile(id: string, path: string): Promise<string> {
const response = await fetch(
`${this.config.clusterUrl}/api/v1/sandboxes/${id}/files?path=${encodeURIComponent(path)}`,
{ headers: { Authorization: `Bearer ${this.config.authToken}` } }
);
const data = await response.json();
return data.content;
}
async dispose(): Promise<void> {}
}
// Register and use
Runra.registerSandboxProvider("my-provider", () => new MySandboxProvider());
FeatureRunra SandboxCubeSandboxDockerE2B
IsolationVMVMContainerVM
Pause/Resume⚠️ Limited
Network Control
Port Exposure
Production Ready
Self-Hosted
Free Tier

Switch providers without changing agent or observability code:

// Development: local Docker
const devRunra = new Runra({
sandbox: { provider: "docker" },
// ... same agent config
});
// Production: hosted Runra
const prodRunra = new Runra({
sandbox: { provider: "runra-sandbox", apiKey: "..." },
// ... same agent config
});
// Self-hosted: CubeSandbox
const selfRunra = new Runra({
sandbox: { provider: "cubesandbox", config: { masterUrl: "..." } },
// ... same agent config
});