Sign in

Moving from Runloop

Move Runloop devboxes to Prized sandboxes: the Prized command for each Runloop call, a short ported script, and what is not on Prized yet.

On this page5

Runloop shuts down on November 13, 2026 (Runloop's notice).

1. Export an API key

A Runloop devbox becomes a Prized sandbox: a microVM that boots in seconds, pauses with its processes running, and forks into live copies. Make a key with Sandboxes: Write on the API keys page, then export it:

export PRIZED_API_KEY=dcp_…

2. Swap each call

RunloopPrized
runloop.devbox.create()prized sandbox create --vcpu 2 --memory 4 --max-runtime 1h
devbox.cmd.exec(command)prized sandbox exec ID -- CMD
devbox.file.write(), file.read()prized sandbox cp ./app.py ID:app.py, and the reverse
devbox.suspend(), devbox.resume()prized sandbox pause ID --wait, prized sandbox resume ID
devbox.snapshot_disk(), then create_devbox()prized sandbox fork ID --count 3, live copies of a running sandbox
devbox.shutdown()prized sandbox rm ID

Each command has an HTTP route that takes the same key (Sandbox API).

3. Port a script

The same steps in Python with requests:

prized_job.py
import os, time, requests

API = "https://api.prized.dev/api/v1"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['PRIZED_API_KEY']}"

def run(box, command):
    return s.post(f"{box['toolboxBase']}/process/execute", json={"command": command}, timeout=600)

def ready(sandbox_id):  # running, and its first command answers
    while (box := s.get(f"{API}/sandboxes/{sandbox_id}").json()["sandbox"])["state"] == "starting":
        time.sleep(1)
    for _ in range(10):
        if run(box, "true").ok:
            return box
        time.sleep(1)

box = ready(s.post(f"{API}/sandboxes", json={"template": "prized-base-v1", "resources": {"vcpu": 2, "memoryMb": 4096}, "maxRuntimeMin": 60}).json()["sandbox"]["id"])
ids = [box["id"]]
try:
    print(run(box, "python3 --version").json()["stdout"])
    s.post(f"{API}/sandboxes/{box['id']}/pause", params={"wait": "paused"})
    s.post(f"{API}/sandboxes/{box['id']}/resume", params={"wait": "running"})
    ids += [copy["id"] for copy in s.post(f"{API}/sandboxes/{box['id']}/fork", json={"count": 2}).json()["sandboxes"]]
finally:
    for sandbox_id in reversed(ids):  # copies first: a copy still starting fails without its parent
        s.delete(f"{API}/sandboxes/{sandbox_id}")

A failed command still answers 200, so read exitCode. A fork answers 409 fork_capacity when the parent's machine is full; start a new sandbox and fork that (Fork).

Not on Prized yet

  • Custom images and root. Sandboxes start from two fixed templates without sudo, so Dockerfile blueprints, apt, and Docker need a box.
  • Long lives and saved snapshots. A sandbox lives 24 hours at most, and a fork copies a running sandbox, not a saved snapshot.
  • Tunnels, SSH, named shells, network policies, Agent Gateways, and Axons.

Getting help moving

Email with what you run on Runloop, and we will help you map it.