Use cases
Short, concrete starting points for each MicroVM feature. Panel steps are given where the panel is the natural way to do the task; REST API examples use curl with your account’s bearer API token (Account > API Tokens) against https://<your panel host>/api/microvm/..., except where noted.
Sandboxes (E2B API)
Section titled “Sandboxes (E2B API)”AI code interpreter
Section titled “AI code interpreter”An agent needs to run Python it just wrote and see the real output before answering. Create a sandbox per conversation, run the code, read stdout, kill it when the conversation ends.
from e2b import Sandbox
sbx = Sandbox.create("sandbox-base", metadata={"location_id": "<location id>"})result = sbx.commands.run("python3 -c \"print(sum(range(100)))\"")print(result.stdout)sbx.kill()Grading untrusted student code
Section titled “Grading untrusted student code”A coursework platform runs student-submitted code that must not see other students’ files or the internet. One sandbox per submission, killed immediately after, keeps a bad submission from affecting anything else.
sbx = Sandbox.create("sandbox-base", metadata={"location_id": "<location id>"}, timeout=30)sbx.files.write("/home/user/solution.py", submission_source)result = sbx.commands.run("python3 /home/user/solution.py < /home/user/input.txt", timeout=10)grade(result.stdout, expected_output)sbx.kill()Data-analysis agent over an uploaded CSV
Section titled “Data-analysis agent over an uploaded CSV”A user uploads a CSV and asks questions about it. Write the file into a sandbox once, then let the agent run as many pandas queries against it as it needs within one sandbox’s lifetime.
sbx = Sandbox.create("sandbox-base", metadata={"location_id": "<location id>"}, timeout=600)sbx.files.write("/home/user/data.csv", csv_bytes)result = sbx.commands.run("python3 -c \"import pandas as pd; print(pd.read_csv('/home/user/data.csv').describe())\"")print(result.stdout)# further commands.run() calls reuse the same sbx for follow-up questionssbx.kill()Browser-less scraping worker
Section titled “Browser-less scraping worker”A short-lived worker fetches and parses a handful of pages with plain HTTP, no browser needed. A tight timeout means an unresponsive site cannot pin the sandbox open.
sbx = Sandbox.create("sandbox-base", metadata={"location_id": "<location id>"}, timeout=60)result = sbx.commands.run("curl -s https://example.com | grep -o '<title>[^<]*'")print(result.stdout)sbx.kill()Images
Section titled “Images”Custom toolchain image from a Dockerfile
Section titled “Custom toolchain image from a Dockerfile”Your agent’s code interpreter needs numpy, pandas and ffmpeg preinstalled so every sandbox boots ready to work, instead of installing them on every run.
Panel: MicroVM > Images > Build image, choose Dockerfile, pick sandbox-base as the base image (required so the image keeps envd), and write:
FROM sandbox-baseRUN apt-get update && apt-get install -y ffmpeg && pip install numpy pandasREST equivalent:
curl -s -X POST https://<your panel host>/api/microvm/images \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "name": "data-toolchain", "source_kind": "dockerfile", "source": {"dockerfile": "FROM sandbox-base\nRUN apt-get update && apt-get install -y ffmpeg && pip install numpy pandas\n"}, "base_image_id": "<sandbox-base image id>", "location_id": "<location id>" }'OCI import of an internal image
Section titled “OCI import of an internal image”You already build and push an image to your own registry as part of CI. Import it directly instead of re-describing it as a Dockerfile.
curl -s -X POST https://<your panel host>/api/microvm/images \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "name": "internal-api", "source_kind": "oci", "source": {"image": "registry.example.com/acme/internal-api:latest"}, "auth": {"kind": "registry", "username": "ci", "password": "<registry password>"}, "location_id": "<location id>" }'Git-built service image with runtime defaults
Section titled “Git-built service image with runtime defaults”A small internal service builds from a Git repository on every release and always needs the same three environment variables and a run lifecycle hook that starts the process on port 8080.
curl -s -X POST https://<your panel host>/api/microvm/images \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "name": "internal-service", "source_kind": "git", "source": {"repo": "https://github.com/acme/internal-service", "branch": "main"}, "location_id": "<location id>", "env": "{\"NODE_ENV\":\"production\",\"PORT\":\"8080\"}", "lifecycle_hooks": {"port": 8080, "run": {"enabled": true, "timeout": 30, "payload": "npm start"}} }'MicroVMs
Section titled “MicroVMs”Per-tenant worker with a lifetime cap
Section titled “Per-tenant worker with a lifetime cap”A SaaS backend spins up one MicroVM per customer job with a hard 2-hour ceiling so a runaway job cannot bill indefinitely; when it hits the cap it pauses rather than losing work.
curl -s -X POST https://<your panel host>/api/microvm/vms \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "location_id": "<location id>", "image_id": "<image id>", "name": "job-4821", "max_lifetime_seconds": 7200, "on_timeout": "pause" }'Preview environment with HTTP ingress and a custom domain
Section titled “Preview environment with HTTP ingress and a custom domain”Every pull request gets its own MicroVM, reachable at pr-142.preview.example.com once you point a CNAME at the generated hostname.
curl -s -X POST https://<your panel host>/api/microvm/vms \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "location_id": "<location id>", "image_id": "<image id>", "name": "pr-142", "ingress": {"http": {"enabled": true, "port": 3000}}, "domain": "pr-142.preview.example.com" }'Public MicroVM on a reserved static IP
Section titled “Public MicroVM on a reserved static IP”A service needs the same public address every time it redeploys, instead of a fresh IPv4 on every create. Reserve a static IP in the location once, then map it on create.
curl -s -X POST https://<your panel host>/api/microvm/vms \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "location_id": "<location id>", "image_id": "<image id>", "name": "fixed-ip-svc", "network": [{"kind": "public", "static_ip_id": "<static ip id>"}] }'Killing or deleting this MicroVM returns the address to Allocated on your account rather than releasing it back to the pool, so the next MicroVM you map it to gets the same address. See Networking.
Always-on webhook receiver
Section titled “Always-on webhook receiver”A small service must never idle-pause between webhook deliveries that can be hours apart, and should come back on its own if it ever crashes.
curl -s -X POST https://<your panel host>/api/microvm/vms \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "location_id": "<location id>", "image_id": "<image id>", "name": "webhook-receiver", "always_on": true, "ingress": {"http": {"enabled": true, "port": 8080, "health_path": "/health"}} }'Scheduled batch job via the REST API
Section titled “Scheduled batch job via the REST API”A nightly report generator: your own scheduler calls the create endpoint once a night, waits for the run to finish, reads the log, then deletes the MicroVM instead of leaving it around.
VM=$(curl -s -X POST https://<your panel host>/api/microvm/vms \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{"location_id":"<location id>","image_id":"<batch image id>","name":"nightly-report","max_lifetime_seconds":1800,"on_timeout":"kill"}' \ | jq -r '.microvm.id')
sleep 60 && curl -s https://<your panel host>/api/microvm/vm/$VM/logs -H "Authorization: Bearer <token>"curl -s -X DELETE https://<your panel host>/api/microvm/vm/$VM -H "Authorization: Bearer <token>"Connectors and CI runners
Section titled “Connectors and CI runners”Ephemeral GitHub Actions runner
Section titled “Ephemeral GitHub Actions runner”Jobs on self-hosted need somewhere isolated to run instead of a shared long-lived runner. A connector with a warm pool of 1 keeps a runner ready so the first job of the day is not slower than the rest.
Panel: MicroVM > Connectors > Add connector, choose the GitHub App card, pick the installed GitHub account, set Labels to microvm, Max concurrent to 5 and Warm pool size to 1. In your workflow: runs-on: [self-hosted, microvm].
GitLab runner
Section titled “GitLab runner”The same pattern for a GitLab project, authenticated with a runner token instead of an app install.
curl -s -X POST https://<your panel host>/api/microvm/connectors/gitlab \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{ "name": "gitlab-runner", "gitlab_url": "https://gitlab.com", "gitlab_token": "glrt-xxxxxxxxxxxxxxxxxxxx", "gitlab_tag_list": ["microvm"], "location_id": "<location id>", "max_concurrent": 5, "warm_count": 1 }'PR preview builds
Section titled “PR preview builds”Reuse a connector’s warm pool for a build-and-deploy job that also opens an HTTP port, then let the job itself call the MicroVMs API (see Preview environment above) to publish the result once the build finishes.
API keys
Section titled “API keys”Per-team keys
Section titled “Per-team keys”Two teams share one account but should not be able to break each other’s sandboxes. Create one MicroVM API key per team from MicroVM > API keys > Create key, named for the team, and give each team only its own key.
CI-only key with revocation
Section titled “CI-only key with revocation”A pipeline needs a key that is easy to identify and to cut off without touching any human’s key.
curl -s -X POST https://<your panel host>/api/microvm/api-keys \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{"name": "ci-pipeline"}'# {"success":true,"key":{"id":"...","name":"ci-pipeline","prefix":"vc_sb_..."},"plaintext":"vc_sb_..."}# store .plaintext now (shown once); keep .key.id for revocation later.
curl -s -X DELETE https://<your panel host>/api/microvm/api-key/<key id> \ -H "Authorization: Bearer <token>"Key rotation
Section titled “Key rotation”Create the replacement key first, update every consumer to use it, then revoke the old one, so there is no window where nothing works.
NEW=$(curl -s -X POST https://<your panel host>/api/microvm/api-keys \ -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \ -d '{"name": "ci-pipeline-2026-09"}')echo "$NEW" | jq -r '.plaintext' # roll this out to every consumer, confirm it works, then:curl -s -X DELETE https://<your panel host>/api/microvm/api-key/<old key id> \ -H "Authorization: Bearer <token>"
