PandaStack

CI runners

Run untrusted PR CI inside ephemeral Firecracker microVMs — no sandbox-escape risk.

Use PandaStack as a GitHub Actions / GitLab CI runner backend. Each job gets a fresh microVM, runs, and dies — no risk of one PR's test suite affecting another's, no shared state, no escape vectors common to Docker-in-Docker.

Pattern: webhook → sandbox

from fastapi import FastAPI, Request
from pandastack import Sandbox

app = FastAPI()

@app.post("/ci/webhook")
async def webhook(req: Request):
    payload = await req.json()
    repo = payload["repository"]["clone_url"]
    sha  = payload["after"]

    with Sandbox(template=pick_template(payload), cpu=4, memory_mb=8192) as sb:
        sb.run(f"git clone --depth 1 --branch {sha} {repo} /workspace", timeout=120)

        # Cache deps using a shared read-only volume
        sb.attach_volume("vol_deps_cache", mount="/cache", readonly=True)

        result = sb.stream(
            "cd /workspace && make ci",
            on_stdout=lambda c: upload_log(payload, "stdout", c),
            on_stderr=lambda c: upload_log(payload, "stderr", c),
        )

        report_status(payload, "success" if result.exit_code == 0 else "failure")

Pattern: matrix builds via fork

Build one "base" sandbox with dependencies installed, then fork for each matrix cell:

base = Sandbox(template="node-js")
base.run("git clone https://... /app && cd /app && npm ci", timeout=300)

snap = base.snapshot()
base.close()

# Now run 8 matrix cells in parallel, each starting from the snapshot
def matrix_cell(node_version: str):
    sb = Sandbox.from_snapshot(snap.id)
    try:
        sb.run(f"cd /app && nvm use {node_version} && npm test")
    finally:
        sb.close()

with ThreadPoolExecutor(max_workers=8) as ex:
    list(ex.map(matrix_cell, ["18", "20", "22"]))

The snapshot is created once (npm ci happens once). Each matrix cell restores in ~250 ms.

Compared to GitHub Actions hosted runners

GitHub Actions hostedPandaStack
Cold start30–60 s (provision VM)250 ms (snapshot restore)
Isolationper-job VMper-job microVM
Cost (1 vCPU·min)$0.008~$0.0008 (self-hosted)
Cache reuseactions/cache (slow)read-only volume mount (instant)
Custom hardwareLimitedAny KVM-capable host

On this page