Skip to content

Quick Start

Get Tako VM running and execute your first code.

Install and Start

pip install "tako-vm[server]"
tako-vm setup                   # pull the executor Docker image
tako-vm server                  # starts on http://localhost:8000

tako-vm server auto-starts a PostgreSQL container on port 55432 for job persistence. Use --port to change the server port:

tako-vm server --port 9000

gVisor on Linux

Tako VM defaults to permissive mode, which falls back to runc if gVisor is not installed. For production, set security_mode: strict to require gVisor. See Security for installation instructions.

Security: Environment Variables

Do not pass secrets (API keys, tokens, passwords) as job type environment variables. User code can read them via /proc/self/environ. Pass sensitive data through input_data instead, which is scoped to a single job. See Security Mitigations for details.

Execute Code

Using curl

curl -X POST http://localhost:8000/execute \
  -H "Content-Type: application/json" \
  -d '{"code": "print(1 + 1)"}'

Using Python

import requests

code = """
import json

# Read input from /input/data.json
with open("/input/data.json") as f:
    data = json.load(f)

# Process
result = {
    "sum": data["x"] + data["y"],
    "product": data["x"] * data["y"]
}

# Write output to /output/result.json
with open("/output/result.json", "w") as f:
    json.dump(result, f)

print("Done!")
"""

response = requests.post(
    "http://localhost:8000/execute",
    json={
        "code": code,
        "input_data": {"x": 10, "y": 20},
        "requirements": ["numpy"]  # Optional: ad-hoc dependencies
    }
)

result = response.json()
print(f"Success: {result['success']}")
print(f"Output: {result['output']}")
print(f"Stdout: {result['stdout']}")

Sync vs Async Execution

Endpoint Behavior Best For
POST /execute Blocks until complete, returns result directly Quick jobs (<30s), simple scripts
POST /execute/async Returns job_id immediately, poll for result Long jobs, queued workloads, production

Sync (Blocking)

# Blocks until done - simple but ties up the connection
response = requests.post("http://localhost:8000/execute", json={
    "code": "print('hello')"
})
print(response.json()["stdout"])  # "hello"

Async (Non-blocking)

# Returns immediately with job_id
response = requests.post("http://localhost:8000/execute/async", json={
    "code": "import time; time.sleep(10); print('done')"
})
job_id = response.json()["job_id"]

# Option 1: Poll for status
status = requests.get(f"http://localhost:8000/jobs/{job_id}").json()
print(status["status"])  # "running" or "succeeded"

# Option 2: Block until complete (with timeout)
result = requests.get(
    f"http://localhost:8000/jobs/{job_id}/result?wait=true&timeout=30"
).json()
print(result["stdout"])  # "done"

When to use async

Use async (/execute/async) when:

  • Jobs may take more than a few seconds
  • You need idempotency keys for safe retries
  • You want to submit multiple jobs and collect results later
  • You're building a production system with job queuing

Understanding the Execution Model

Your code runs in an isolated Docker container with three mounted directories:

Path Permission Purpose
/input/data.json Read-only Input data (JSON)
/output/ Read-write Output files
/code/main.py Read-only Your code
Container Filesystem
├── /input/
│   └── data.json      ← Your input_data as JSON
├── /output/
│   └── result.json    ← Write your output here
├── /code/
│   └── main.py        ← Your code
└── /tmp/              ← Temporary files (writable)

Output Files (Artifacts)

All output files must be written to /output/ - this is the only writable directory besides /tmp/.

  • /output/result.json is special: it's automatically parsed and returned in the output field
  • Any other files in /output/ (e.g., report.txt, data.csv) are saved as artifacts
  • Download artifacts via GET /jobs/{job_id}/artifacts/{filename}
  • Files written elsewhere are lost when the container is destroyed

Response Format

{
  "success": true,
  "output": {"sum": 30, "product": 200},
  "stdout": "Done!\n",
  "stderr": "",
  "exit_code": 0,
  "execution_time": 0.35,
  "job_type": "default"
}
Field Description
success Whether execution completed without errors
output Contents of /output/result.json (parsed)
stdout Standard output from your code
stderr Standard error from your code
exit_code Process exit code (0 = success)
execution_time Wall clock time in seconds
job_type Environment used

Next Steps