Production Setup
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
Section titled “API key management”Creating an API key
Section titled “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 variablesexport RUNRA_API_KEY="rsk_live_xxxxxxxxxxxxxxxxxxxx"Key best practices
Section titled “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
Section titled “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
Section titled “Environment configuration”Required variables
Section titled “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
Section titled “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
Section titled “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
Section titled “Resource planning”Sizing guidelines
Section titled “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
Section titled “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_secondsExample: If you handle 5 agent requests per second, each lasting 120 seconds on average:
5 req/s × 120s = 600 concurrent sandboxesCapacity tiers
Section titled “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
Section titled “Concurrency limits”Client-side rate limiting
Section titled “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
Section titled “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); }}
// Usageconst pool = new SandboxPool(runra, 20, { image: "node:22", resources: { cpu: 2, memoryMb: 4096 },});await pool.initialize();
// Acquire and release during request handlingconst sandbox = await pool.acquire();try { await sandbox.exec("npm test");} finally { await pool.release(sandbox);}Template selection
Section titled “Template selection”Available templates
Section titled “Available templates”Runra provides pre-built templates for common environments:
// Use a named templateconst sandbox = await runra.sandboxes.create({ template: "node-typescript",});
// Or specify an image directlyconst sandbox = await runra.sandboxes.create({ image: "node:22",});Common templates
Section titled “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
Section titled “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
Section titled “Error handling”Common errors
Section titled “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
Section titled “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
Section titled “Next steps”- Running Code — execute commands in sandboxes
- Observability — monitor production sandbox usage
- SDK Usage — full SDK reference