Sandbox Providers
Sandbox infrastructure providers for Runra Runtime — Runra Sandbox (hosted), CubeSandbox (self-hosted), Docker, E2B, and custom provider interface
Runra Runtime abstracts sandbox infrastructure behind a pluggable provider interface. Choose hosted, self-hosted, or local providers — your agent code stays the same.
Built-in Providers
| Provider | Key | Use Case |
|---|---|---|
| Runra Sandbox | runra-sandbox | Hosted VM-backed sandboxes (production) |
| CubeSandbox | cubesandbox | Self-hosted VM-backed sandboxes |
| Docker | docker | Local Docker containers (development) |
| E2B | e2b | E2B sandbox infrastructure |
Runra Sandbox (Hosted)
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
});
Available Images
| Image | Description |
|---|---|
node:22 | Node.js 22 with npm, yarn, pnpm |
node:20 | Node.js 20 LTS |
python:3.12 | Python 3.12 with pip, uv |
python:3.11 | Python 3.11 |
ubuntu:24.04 | Bare Ubuntu 24.04 |
ubuntu:22.04 | Bare Ubuntu 22.04 |
custom | BYO image (contact support) |
Features
- VM-level isolation per sandbox
- Pause & resume preserves full filesystem state
- Configurable network policies
- Automatic cleanup via
timeoutMsandidleTimeoutMs - Up to 8 vCPUs and 16 GB RAM per sandbox
- Expose ports for previews and dev servers
- Full API reference
CubeSandbox (Self-Hosted)
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 },
},
},
});
Setup
Self-hosting CubeSandbox requires:
- A CubeMaster control plane
- One or more Cubelet compute nodes
- Image templates built on each node
See the CubeSandbox repository for setup instructions.
Docker (Local Development)
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 },
},
},
});
Resource Mapping
Docker provider maps Runra resources to Docker limits:
| Runra | Docker |
|---|---|
cpu: 2 | --cpus=2 |
memoryMb: 4096 | --memory=4096m |
diskMb: 10240 | --storage-opt size=10G |
timeoutMs: 300000 | --stop-timeout |
Limitations
- No VM-level isolation (shared kernel)
- No pause/resume (simulated via
docker pausewhich freezes processes) - File system not preserved on terminate
- Suitable for development only, not production
E2B
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,
},
},
});
Provider Configuration
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";
};
};
};
}
Custom Provider
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());
Provider Comparison
| Feature | Runra Sandbox | CubeSandbox | Docker | E2B |
|---|---|---|---|---|
| Isolation | VM | VM | Container | VM |
| Pause/Resume | ✅ | ✅ | ⚠️ Limited | ✅ |
| Network Control | ✅ | ✅ | ✅ | ✅ |
| Port Exposure | ✅ | ✅ | ✅ | ✅ |
| Production Ready | ✅ | ✅ | ❌ | ✅ |
| Self-Hosted | ❌ | ✅ | ✅ | ❌ |
| Free Tier | ✅ | ✅ | ✅ | ❌ |
Switching Providers
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
});
Next Steps
- Runtime Architecture — how providers fit in
- Observability — export sandbox events
- Configuration Reference — all options