Production Setup
Production setup guide for Runra Sandbox — API key management, environment configuration, resource planning, concurrency limits, and template selection
This guide covers everything you need to configure Runra Sandbox for production workloads. Follow these steps to ensure reliable, secure, and cost-effective sandbox operations.
API key management
Creating an API key
API keys are managed through the Runra Dashboard. Each key is scoped to an organization and can have fine-grained permissions.
# Always load your API key from environment variables
export RUNRA_API_KEY="rsk_live_xxxxxxxxxxxxxxxxxxxx"
Key best practices
- Use separate keys per environment: Dev, staging, and production should each have their own API key.
- Rotate keys regularly: Generate new keys and deactivate old ones on a schedule.
- Never commit keys: Use a secrets manager or environment variables.
- Scope keys by sandbox type: Create restricted keys that can only create sandboxes with specific resources.
Multiple API keys
If you manage sandboxes for different applications or tenants, use separate Runra clients:
const appA = new Runra({
sandbox: { provider: "runra-sandbox", apiKey: process.env.APP_A_KEY },
});
const appB = new Runra({
sandbox: { provider: "runra-sandbox", apiKey: process.env.APP_B_KEY },
});
Environment configuration
Required variables
| Variable | Description | Example |
|---|---|---|
RUNRA_API_KEY | Your Runra API key | rsk_live_... |
RUNRA_API_URL | API base URL | https://api.box.runra.dev |
Recommended variables
| Variable | Description | Example |
|---|---|---|
RUNRA_DEFAULT_IMAGE | Default sandbox image | node:22 |
RUNRA_DEFAULT_CPU | Default CPU allocation | 2 |
RUNRA_DEFAULT_MEMORY_MB | Default memory allocation | 4096 |
RUNRA_DEFAULT_TIMEOUT_MS | Default sandbox timeout | 300000 |
Programmatic configuration
Prefer programmatic configuration for more control:
import { Runra } from "@runra/sdk";
const runra = new Runra({
sandbox: {
provider: "runra-sandbox",
apiKey: process.env.RUNRA_API_KEY!,
apiUrl: process.env.RUNRA_API_URL ?? "https://api.box.runra.dev",
defaults: {
image: "node:22",
resources: {
cpu: 2,
memoryMb: 4096,
diskMb: 10240,
},
timeoutMs: 300_000,
idleTimeoutMs: 120_000,
},
},
observability: {
provider: "axiom",
token: process.env.AXIOM_TOKEN,
dataset: "runra-production",
},
});
Resource planning
Sizing guidelines
Choose resources based on your workload:
| Workload | CPU | Memory | Disk | Typical use |
|---|---|---|---|---|
| Light scripting | 2 | 2048 MB | 5120 MB | Simple scripts, API calls |
| Web development | 2 | 4096 MB | 10240 MB | npm install, build, test |
| Data processing | 4 | 8192 MB | 20480 MB | Python data pipelines |
| Full-stack agent | 4 | 8192 MB | 20480 MB | Claude Code, Codex agents |
| Heavy ML/compilation | 8 | 16384 MB | 51200 MB | Model training, large builds |
Estimating concurrency
This is the most important production planning step. Determine your peak concurrent sandbox count:
concurrent_sandboxes = peak_requests_per_second × average_sandbox_lifetime_seconds
Example: If you handle 5 agent requests per second, each lasting 120 seconds on average:
5 req/s × 120s = 600 concurrent sandboxes
Capacity tiers
| Tier | Max concurrent sandboxes | Suitable for |
|---|---|---|
| Starter | 50 | Prototyping, small teams |
| Pro | 500 | Production SaaS applications |
| Enterprise | Custom | High-scale platforms |
Contact sales@runra.dev to discuss enterprise capacity.
Concurrency limits
Client-side rate limiting
Implement exponential backoff to handle rate limits gracefully:
async function createSandboxWithRetry(
runra: Runra,
config: SandboxConfig,
maxRetries = 5
): Promise<Sandbox> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await runra.sandboxes.create(config);
} catch (error) {
if (error.code === "RATE_LIMITED" && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.warn(`Rate limited. Retrying in ${delay}ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
throw new Error("Max retries exceeded");
}
Concurrency pools
Manage a pool of available sandboxes for better performance:
class SandboxPool {
private available: Sandbox[] = [];
private inUse = new Set<string>();
constructor(
private runra: Runra,
private poolSize: number,
private config: SandboxConfig
) {}
async initialize() {
this.available = await Promise.all(
Array.from({ length: this.poolSize }, () =>
this.runra.sandboxes.create(this.config)
)
);
}
async acquire(): Promise<Sandbox> {
if (this.available.length === 0) {
// Pool exhausted — create on demand
return this.runra.sandboxes.create(this.config);
}
const sandbox = this.available.pop()!;
this.inUse.add(sandbox.id);
return sandbox;
}
async release(sandbox: Sandbox) {
this.inUse.delete(sandbox.id);
// Reset sandbox to clean state
await sandbox.exec("rm -rf /workspace/*");
this.available.push(sandbox);
}
}
// Usage
const pool = new SandboxPool(runra, 20, {
image: "node:22",
resources: { cpu: 2, memoryMb: 4096 },
});
await pool.initialize();
// Acquire and release during request handling
const sandbox = await pool.acquire();
try {
await sandbox.exec("npm test");
} finally {
await pool.release(sandbox);
}
Template selection
Available templates
Runra provides pre-built templates for common environments:
// Use a named template
const sandbox = await runra.sandboxes.create({
template: "node-typescript",
});
// Or specify an image directly
const sandbox = await runra.sandboxes.create({
image: "node:22",
});
Common templates
| Template | Image | Includes |
|---|---|---|
node-basic | node:22 | Node.js 22, npm, npx |
node-typescript | node:22 | Node.js + TypeScript, ts-node, tsx |
python-basic | python:3.12 | Python 3.12, pip |
python-ml | python:3.12 | Python + numpy, pandas, scikit-learn |
fullstack | node:22 | Node.js + Python + git + curl |
ubuntu-base | ubuntu:24.04 | Clean Ubuntu with build-essential |
Custom templates
For custom environments, use a Dockerfile and specify the image:
const sandbox = await runra.sandboxes.create({
image: "my-registry/my-custom-image:latest",
// Custom images must be pre-loaded; contact support for setup
});
Error handling
Common errors
| Error code | Description | Action |
|---|---|---|
RATE_LIMITED | Too many requests | Implement backoff |
RESOURCE_EXHAUSTED | No capacity available | Retry or reduce concurrency |
INVALID_CONFIG | Bad sandbox configuration | Check your create options |
SANDBOX_TIMEOUT | Sandbox exceeded timeout | Increase timeout or optimize workload |
IMAGE_NOT_FOUND | Template/image doesn't exist | Verify image name |
QUOTA_EXCEEDED | Account quota hit | Upgrade plan or terminate idle sandboxes |
Graceful degradation
async function safeCreateSandbox(config: SandboxConfig): Promise<Sandbox | null> {
try {
return await runra.sandboxes.create(config);
} catch (error) {
switch (error.code) {
case "RESOURCE_EXHAUSTED":
// Try a smaller sandbox
return runra.sandboxes.create({
...config,
resources: { cpu: 2, memoryMb: 2048 },
});
case "RATE_LIMITED":
// Queue for later
await enqueueSandboxRequest(config);
return null;
default:
throw error;
}
}
}
Next steps
- Running Code — execute commands in sandboxes
- Observability — monitor production sandbox usage
- SDK Usage — full SDK reference