Exposing Ports
Exposing ports allows you to access services running inside your sandbox — dev servers, web apps, APIs, databases, and preview environments — from outside the sandbox.
Basic port exposure
Section titled “Basic port exposure”Use exposePort() to make a sandbox service publicly accessible:
const sandbox = await runra.sandboxes.create({ image: "node:22", resources: { cpu: 2, memoryMb: 4096 },});
// Start a dev server inside the sandboxawait sandbox.exec("npx create-react-app my-app");await sandbox.exec("cd my-app && npm start &");
// Expose port 3000const port = await sandbox.exposePort(3000);console.log(port.url);// https://3000-abc123.box.runra.devURL format
Section titled “URL format”Exposed ports follow this URL pattern:
https://{port}-{sandboxId}.{domain}| Component | Example | Description |
|---|---|---|
{port} | 3000 | The exposed port number |
{sandboxId} | abc123 | Your sandbox ID (first 6 chars) |
{domain} | box.runra.dev | The Runra Sandbox domain |
Full URL: https://3000-abc123.box.runra.dev
The Port type
Section titled “The Port type”interface Port { port: number; // The port number exposed url: string; // Public HTTPS URL to access the port sandboxId: string; // The sandbox this port belongs to}Multiple ports
Section titled “Multiple ports”Expose multiple ports for full-stack applications:
// Start frontend and backendawait sandbox.exec("cd frontend && npm run dev &");await sandbox.exec("cd backend && npm run dev &");
// Expose bothconst frontend = await sandbox.exposePort(3000);const backend = await sandbox.exposePort(8000);const storybook = await sandbox.exposePort(6006);
console.log(frontend.url); // https://3000-abc123.box.runra.devconsole.log(backend.url); // https://8000-abc123.box.runra.devconsole.log(storybook.url); // https://6006-abc123.box.runra.devPort ranges
Section titled “Port ranges”You can expose ports in the range 1024–65535. Ports below 1024 are reserved for system services.
// Valid port rangeconst port = await sandbox.exposePort(8080); // ✅ Validconst port = await sandbox.exposePort(443); // ❌ Reserved (below 1024)const port = await sandbox.exposePort(99999); // ❌ Invalid (above 65535)Checking exposed ports
Section titled “Checking exposed ports”List all currently exposed ports in a sandbox:
const ports = await sandbox.getPorts();for (const port of ports) { console.log(`Port ${port.port}: ${port.url}`);}Closing ports
Section titled “Closing ports”Close an exposed port when it’s no longer needed:
await sandbox.closePort(3000);// Port 3000 is no longer publicly accessiblePorts are automatically closed when:
- The sandbox is paused
- The sandbox is terminated
- You explicitly call
closePort()
Use cases
Section titled “Use cases”Dev server previews
Section titled “Dev server previews”Preview web applications during development:
const sandbox = await runra.sandboxes.create({ image: "node:22" });await sandbox.exec("git clone https://github.com/acme/webapp.git");await sandbox.exec("cd webapp && npm install && npm run dev &");
const preview = await sandbox.exposePort(5173);console.log(`Preview available at: ${preview.url}`);// Share this URL with your team for instant feedbackBrowser-based workflows
Section titled “Browser-based workflows”Agents can expose browser automation UIs:
const sandbox = await runra.sandboxes.create({ image: "ubuntu:24.04" });
// Start a VNC-based browser for agent useawait sandbox.exec("Xvfb :99 &");await sandbox.exec("novnc --vnc localhost:5900 &");
const browserUI = await sandbox.exposePort(6080);console.log(`Browser UI: ${browserUI.url}`);API endpoints for testing
Section titled “API endpoints for testing”Expose API endpoints so your CI or test suite can interact with sandboxed services:
// Run a mock API inside the sandboxawait sandbox.exec("cd mock-api && node server.js &");
const api = await sandbox.exposePort(4000);
// External test can now hit the APIconst response = await fetch(`${api.url}/users`);const users = await response.json();expect(users).toHaveLength(3);Temporary staging environments
Section titled “Temporary staging environments”Create on-demand staging environments with exposed ports:
async function createStagingEnv(branch: string): Promise<string> { const sandbox = await runra.sandboxes.create({ image: "node:22" }); await sandbox.exec(`git clone -b ${branch} https://github.com/acme/app.git`); await sandbox.exec("cd app && docker-compose up -d");
const app = await sandbox.exposePort(3000); const db = await sandbox.exposePort(5432);
console.log(`Staging env for ${branch}:`); console.log(` App: ${app.url}`); console.log(` DB: ${db.url}`);
return sandbox.id;}Multi-service architectures
Section titled “Multi-service architectures”Run and expose microservices inside a single sandbox:
await sandbox.exec("docker-compose up -d");
// Expose all servicesconst services = { frontend: await sandbox.exposePort(3000), api: await sandbox.exposePort(4000), admin: await sandbox.exposePort(4001), metrics: await sandbox.exposePort(9090),};
for (const [name, port] of Object.entries(services)) { console.log(`${name}: ${port.url}`);}Security
Section titled “Security”Authentication
Section titled “Authentication”Exposed ports are public by default. For additional security:
// Use basic auth on the service itselfawait sandbox.exec( "node server.js --auth-user=admin --auth-pass=${ADMIN_PASS}", { env: { ADMIN_PASS: generateToken() } });Access control
Section titled “Access control”Port URLs include the sandbox ID, making them unguessable. However, treat port URLs as sensitive and:
- Don’t share URLs in public channels without auth
- Close ports immediately when no longer needed
- Use short sandbox lifetimes for preview environments
Network policy
Section titled “Network policy”Control which ports can be exposed:
const sandbox = await runra.sandboxes.create({ image: "node:22", network: { ingress: { allowPorts: [3000, 5173, 8080], // Only these ports can be exposed }, },});
await sandbox.exposePort(3000); // ✅ Allowedawait sandbox.exposePort(9000); // ❌ Blocked by network policyLifecycle
Section titled “Lifecycle”Ports follow the sandbox lifecycle:
| Sandbox state | Port status |
|---|---|
running | Ports are active and accessible |
paused | Ports are closed, URLs stop resolving |
resumed | Ports are automatically re-exposed with same URLs |
terminated | Ports are permanently closed |
const sandbox = await runra.sandboxes.create({ image: "node:22" });await sandbox.exec("python -m http.server 8000 &");const port = await sandbox.exposePort(8000);
await sandbox.pause();// port.url is now unreachable
const resumed = await runra.sandboxes.resume(sandbox.id);// port.url is accessible again with the same URLNext steps
Section titled “Next steps”- Running Code — start services inside sandboxes
- Isolation Model — network isolation details
- Observability — track port exposure events