Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Quickstart

In five minutes you'll have a Linux microVM running on a ScaiBunker worker, you'll execute a command in it, drop a file into it, and tear it down.

You need:

  • A ScaiGrid API key with scaibunker:create and scaibunker:execute (any tenant admin has these).
  • The managed endpoint, or a self-hosted ScaiGrid with at least one worker registered.
bash
1
2
export SCAIGRID_HOST="https://scaigrid.scailabs.ai"
export SCAIGRID_API_KEY="sgk_..."

1. Create the bunker#

POST to /bunkers with the image, lifecycle mode, network profile, and resource request. Defaults are sensible for a one-off Python workload.

bash
1
2
3
4
curl -X POST "$SCAIGRID_HOST/v1/modules/scaibunker/bunkers" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image":"python-3.12","lifecycle_mode":"ephemeral","network_profile":"registry","cpu_millicores":1000,"memory_mb":512,"disk_mb":1024}'
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import httpx, os
HOST = os.environ["SCAIGRID_HOST"]
H = {"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"}

bunker = httpx.post(
    f"{HOST}/v1/modules/scaibunker/bunkers",
    headers=H,
    json={
        "image": "python-3.12",
        "lifecycle_mode": "ephemeral",
        "network_profile": "registry",
        "cpu_millicores": 1000,
        "memory_mb": 512,
        "disk_mb": 1024,
    },
).json()["data"]
print(bunker["id"], bunker["status"])
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const HOST = process.env.SCAIGRID_HOST;
const H = { "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}` };

const res = await fetch(`${HOST}/v1/modules/scaibunker/bunkers`, {
  method: "POST",
  headers: { ...H, "Content-Type": "application/json" },
  body: JSON.stringify({
    image: "python-3.12",
    lifecycle_mode: "ephemeral",
    network_profile: "registry",
    cpu_millicores: 1000,
    memory_mb: 512,
    disk_mb: 1024,
  }),
});
const { data: bunker } = await res.json();
console.log(bunker.id, bunker.status);

Save the returned bunker.id — every subsequent call references it. Status starts at pending and flips to running once the worker has booted the microVM (usually under a second on a warm worker).

2. Run a command#

Send a shell command to the bunker. The registry profile lets pip reach PyPI; the install takes a few seconds on a warm worker.

bash
1
2
3
4
curl -X POST "$SCAIGRID_HOST/v1/modules/scaibunker/bunkers/$BUNKER_ID/exec" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command":"pip install --quiet pandas && python -c \"import pandas; print(pandas.__version__)\"","timeout_s":120}'
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
result = httpx.post(
    f"{HOST}/v1/modules/scaibunker/bunkers/{bunker['id']}/exec",
    headers=H,
    json={
        "command": "pip install --quiet pandas && python -c 'import pandas; print(pandas.__version__)'",
        "timeout_s": 120,
    },
    timeout=180,
).json()["data"]
print(result["exit_code"], result["stdout"])
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const r = await fetch(`${HOST}/v1/modules/scaibunker/bunkers/${bunker.id}/exec`, {
  method: "POST",
  headers: { ...H, "Content-Type": "application/json" },
  body: JSON.stringify({
    command: "pip install --quiet pandas && python -c 'import pandas; print(pandas.__version__)'",
    timeout_s: 120,
    working_dir: "/workspace",
  }),
});
const { data } = await r.json();
console.log(data.exit_code, data.stdout);

You should see the pandas version printed in stdout. The registry profile lets pip reach PyPI; on the isolated profile the install would fail with no route to host.

3. Write and read a file#

PUT and GET against /files/{path} write and read bytes inline. The path is rooted at the bunker's filesystem; conventionally use /workspace for caller-supplied data.

bash
1
2
3
4
5
6
printf "name,score\nalice,42\nbob,17\n" > scores.csv
curl -X PUT "$SCAIGRID_HOST/v1/modules/scaibunker/bunkers/$BUNKER_ID/files/workspace/scores.csv" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" --data-binary @scores.csv

curl "$SCAIGRID_HOST/v1/modules/scaibunker/bunkers/$BUNKER_ID/files/workspace/scores.csv" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"

Files under /workspace are the convention for caller-supplied data.

4. Snapshot before terminating#

To preserve state before tearing down, take a snapshot. Or pass ?snapshot=true on terminate (step 5) to do snapshot-then-destroy in one call.

bash
1
2
curl -X POST "$SCAIGRID_HOST/v1/modules/scaibunker/bunkers/$BUNKER_ID/snapshot" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"

The snapshot id comes back on the bunker record; download the archive later with GET /snapshots/{id}/archive.

5. Terminate#

DELETE on the bunker tears it down and decrements your tenant's quota counters across every bucket the bunker contributed to.

bash
1
2
curl -X DELETE "$SCAIGRID_HOST/v1/modules/scaibunker/bunkers/$BUNKER_ID?snapshot=true" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"

What just happened#

The scheduler picked one worker and booted a Firecracker microVM with the python-3.12 ext4 rootfs, attached to a registry-profile network namespace. Every exec and file call streamed through ScaiGrid to the worker; large outputs go to S3 via the storage proxy. Quota counters in Redis went up on create and back down on terminate. Next: read Architecture, Network profiles, and the data analysis tutorial.

Updated 2026-05-18 15:01:26 View source (.md) rev 12