Docs / guides / setup

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

VariableDescriptionExample
RUNRA_API_KEYYour Runra API keyrsk_live_...
RUNRA_API_URLAPI base URLhttps://api.box.runra.dev
VariableDescriptionExample
RUNRA_DEFAULT_IMAGEDefault sandbox imagenode:22
RUNRA_DEFAULT_CPUDefault CPU allocation2
RUNRA_DEFAULT_MEMORY_MBDefault memory allocation4096
RUNRA_DEFAULT_TIMEOUT_MSDefault sandbox timeout300000

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:

WorkloadCPUMemoryDiskTypical use
Light scripting22048 MB5120 MBSimple scripts, API calls
Web development24096 MB10240 MBnpm install, build, test
Data processing48192 MB20480 MBPython data pipelines
Full-stack agent48192 MB20480 MBClaude Code, Codex agents
Heavy ML/compilation816384 MB51200 MBModel 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

TierMax concurrent sandboxesSuitable for
Starter50Prototyping, small teams
Pro500Production SaaS applications
EnterpriseCustomHigh-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

TemplateImageIncludes
node-basicnode:22Node.js 22, npm, npx
node-typescriptnode:22Node.js + TypeScript, ts-node, tsx
python-basicpython:3.12Python 3.12, pip
python-mlpython:3.12Python + numpy, pandas, scikit-learn
fullstacknode:22Node.js + Python + git + curl
ubuntu-baseubuntu:24.04Clean 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 codeDescriptionAction
RATE_LIMITEDToo many requestsImplement backoff
RESOURCE_EXHAUSTEDNo capacity availableRetry or reduce concurrency
INVALID_CONFIGBad sandbox configurationCheck your create options
SANDBOX_TIMEOUTSandbox exceeded timeoutIncrease timeout or optimize workload
IMAGE_NOT_FOUNDTemplate/image doesn't existVerify image name
QUOTA_EXCEEDEDAccount quota hitUpgrade 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