Docs
ooga gives every agent its own Linux virtual machine. Each sandbox is a full Ubuntu 24.04 VM with its own kernel, public IP and disk, driven from the ooga CLI or the HTTP API.
Install
The installer downloads the CLI for your platform, checks its SHA-256 and puts it in ~/.local/bin. Set OOGA_BIN_DIR to install somewhere else, or OOGA_VERSION=v0.1.0 to pin a version. There is no native Windows build yet: on Windows, use the CLI inside WSL.
Quickstart
ooga is invite-only during the beta. Once you're invited, sign in to the dashboard with Google and create an API key there.
# paste an API key from the dashboard; also registers ~/.ssh/id_ed25519.pub
$ ooga login
# a running VM in about 10 seconds, then a root shell
$ ooga new my-box
$ ooga ssh my-box
# run commands without SSH, from scripts or agents
$ ooga exec my-box -- 'git clone https://github.com/you/app && cd app && make test'
# snapshot the disk and stop billing; ooga start my-box resumes
$ ooga stop my-box
A new sandbox finishes installing its toolchain in the background for a few minutes after it starts. ooga exec my-box -- cat /var/lib/ooga/status prints ready when it's done.
Sandboxes
Each sandbox is a VM with a dedicated public IPv4 and IPv6 address. You log in as root with your SSH keys; password login is disabled. The SSH host key is generated by ooga and pinned by the CLI, so ooga ssh and ooga scp verify the machine they connect to.
Sizes
| Size | vCPU | Memory | Disk |
|---|---|---|---|
small | 2 | 4 GB | 40 GB |
default | 4 | 8 GB | 80 GB |
large | 8 | 16 GB | 160 GB |
xlarge | 16 | 32 GB | 320 GB |
vCPUs are shared. Pick a size with ooga new NAME --size large.
What's installed
Ubuntu 24.04 with Docker (and Compose), Node.js 22, Bun, Python 3.12, Go, Rust, build-essential, git, the GitHub CLI, Google Chrome, tmux, ripgrep and jq, plus the Claude Code, Codex and OpenCode CLIs.
Idle auto-stop
A sandbox that sees no API activity for its TTL (default 1 hour) is stopped automatically, which snapshots its disk first, so nothing is lost. An open ooga ssh session keeps it awake. Set the TTL with ooga new NAME --ttl 4h, or --ttl 0 to never auto-stop.
Pricing
| Size | vCPU / memory / disk | Per hour | Always on, per month |
|---|---|---|---|
small | 2 / 4 GB / 40 GB | $0.025 | about $18 |
default | 4 / 8 GB / 80 GB | $0.035 | about $26 |
large | 8 / 16 GB / 160 GB | $0.06 | about $44 |
xlarge | 16 / 32 GB / 320 GB | $0.12 | about $88 |
- You pay while a sandbox runs. Each run (
new,start,forkorrestore) is billed for at least one hour, then per second. - Stopped sandboxes and automatic snapshots are free. Saved snapshots cost $0.056 per snapshot per day.
- In any month you use ooga, the minimum charge is $20, which includes that much usage. A month without usage costs nothing.
- On your own Hetzner, Hetzner bills you for the servers and ooga charges a platform fee per running hour, billed per second with no minimum: $0.005 (small), $0.01 (default), $0.02 (large), $0.04 (xlarge).
ooga usage shows this month's usage and charges. Add a card with ooga billing before starting sandboxes: it opens a Stripe page in your browser, and ooga never sees your card number. Invoices are charged to the card at the start of each month; ooga billing portal changes the card or downloads invoices.
Snapshots & forks
A snapshot is a copy of the sandbox's whole disk.
- Automatic snapshots are taken every hour while a sandbox runs, and when it stops. The 3 most recent are kept. They're free.
- Saved snapshots are kept until you delete them:
ooga snapshot my-box --name before-upgrade.
$ ooga snapshots my-box # list
$ ooga fork my-box@before-upgrade my-box-2 # new sandbox from a snapshot
$ ooga fork my-box try --count 5 # try-1 … try-5
$ ooga restore my-box@before-upgrade # roll back in place
restore snapshots the current state first, so a restore can itself be undone. ooga stop snapshots the disk and deletes the VM, and ooga start brings it back from that snapshot: the disk is identical, but the IP address changes.
Snapshots copy the full disk, so they take time: in our tests about 35 seconds for a 3 GB disk, longer as the disk fills. Forks and starts take about 20–30 seconds. Stopping takes about 2 minutes.
Coding agents
ooga prompt runs a coding agent inside the sandbox and streams its output to your terminal:
$ ooga prompt my-box "make the failing tests pass"
$ ooga prompt my-box "add a /health endpoint" --agent codex
Agents: claude (default), codex, opencode. Each agent needs its own login inside the sandbox: run ooga ssh my-box and start the agent once to sign in, or export its API key (for example ANTHROPIC_API_KEY) in /root/.bashrc.
You can also run your agent on your own machine and give it the sandbox as a tool: ooga exec and the HTTP API return stdout, stderr and the exit code.
Desktop
$ ooga desktop my-box
Opens the sandbox's desktop (XFCE) in your browser over noVNC, tunnelled through SSH, so nothing is exposed to the internet. The first run installs the desktop, which takes a couple of minutes.
Your own Hetzner
Run sandboxes in your own Hetzner Cloud project instead of ooga's. Hetzner bills you for the servers; ooga charges a platform fee per running hour, billed per second with no minimum.
$ ooga hetzner connect --location nbg1 # prompts for a Read & Write API token
$ ooga new my-box # now created in your project
$ ooga hetzner status
$ ooga hetzner disconnect # after deleting those sandboxes
Create the token in the Hetzner Console under Security → API tokens, ideally in a project used only for ooga. ooga checks the token before saving it and stores it encrypted. ooga creates only servers named ooga-… and snapshots; please manage them with ooga rather than in the console. Locations: fsn1, nbg1, hel1.
CLI reference
ooga login | Save your API key and register your SSH public key. |
ooga new [NAME] | Start a sandbox. --size, --ttl, --count N, --no-wait. |
ooga list · ooga status NAME | Show sandboxes. |
ooga ssh NAME [-- CMD] | Open a shell, or run a command over SSH. |
ooga scp SRC DEST | Copy files: ooga scp ./f box:/root/ or ooga scp box:/root/f .; -r for directories. |
ooga exec NAME -- CMD | Run a command through the API (no SSH needed). --timeout SECS. |
ooga prompt NAME "TEXT" | Run a coding agent and stream its output. --agent claude|codex|opencode. |
ooga desktop NAME | Open the desktop in your browser. --port. |
ooga snapshot NAME | Save a snapshot. --name LABEL; --delete SNAPSHOT. |
ooga snapshots NAME | List snapshots. |
ooga fork NAME[@SNAP] NEW | New sandbox from a snapshot (default: the current state). --count N. |
ooga restore NAME@SNAP | Roll back in place. |
ooga stop NAME · --all | Snapshot and stop billing. |
ooga start NAME | Resume from the latest snapshot. |
ooga delete NAME | Delete a sandbox and all its snapshots. -y skips the prompt. |
ooga keys list|add|rm | Manage the SSH keys allowed into your sandboxes. |
ooga hetzner connect|status|disconnect | Use your own Hetzner project. |
ooga usage [--month YYYY-MM] | This month's usage and charges. |
ooga billing · ooga billing portal | Add a card (opens your browser) · change it, pay or download invoices. |
Every command accepts --json for machine-readable output. The CLI reads OOGA_API_KEY and OOGA_API_URL from the environment, which overrides ~/.config/ooga/config.json.
HTTP API
Base URL https://api.ooga.sh/v1. Authenticate with an API key from the dashboard as a bearer token. Requests and responses are JSON; errors look like {"error": {"code": "...", "message": "..."}}.
$ curl -s https://api.ooga.sh/v1/sandboxes \
-H "Authorization: Bearer $OOGA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "ci-1", "size": "small"}'
$ curl -s https://api.ooga.sh/v1/sandboxes/ci-1/exec \
-H "Authorization: Bearer $OOGA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command": "uname -a", "timeout_secs": 60}'
{"exit_code":0,"stdout":"Linux ooga-… 6.8.0 …\n","stderr":""}
Creating, starting, forking and restoring return 402 Payment Required until a card is on file, or while an invoice is overdue. Creating, stopping, starting, forking and restoring return 202 Accepted and continue in the background; poll GET /v1/sandboxes/{name} until state is running or stopped.
| GET | /sandboxes | List sandboxes |
| POST | /sandboxes | Create: {name?, size?, ttl_secs?, count?} |
| GET | /sandboxes/{name} | Show one |
| DELETE | /sandboxes/{name} | Delete it and its snapshots |
| POST | /sandboxes/{name}/exec | Run a shell command: {command, timeout_secs?} (max 3600) |
| POST | /sandboxes/{name}/prompt | Run an agent to completion: {prompt, agent?, timeout_secs?} |
| POST | /sandboxes/{name}/stop | Snapshot and stop |
| POST | /sandboxes/{name}/start | Start from the latest snapshot |
| POST | /sandboxes/{name}/fork | {name, snapshot?, count?} |
| POST | /sandboxes/{name}/restore | {snapshot} (id or label) |
| GET | /sandboxes/{name}/snapshots | List snapshots |
| POST | /sandboxes/{name}/snapshots | Save one: {label?} |
| DELETE | /sandboxes/{name}/snapshots/{snap} | Delete a snapshot |
| GET | /sandboxes/{name}/ssh | SSH host, user, port and pinned host key |
| POST | /sandboxes/{name}/touch | Reset the idle timer |
| GET · POST | /ssh-keys | List · add {name, public_key} |
| DELETE | /ssh-keys/{id} | Remove a key |
| GET | /account | Your account and limits |
| GET · POST | /api-keys | List · create {label?} (the key is returned once) |
| DELETE | /api-keys/{id} | Revoke a key |
| PUT · DELETE | /account/hetzner | Connect {token, location?} · disconnect your Hetzner project |
| GET | /usage?month=YYYY-MM | Usage and charges |
| GET | /billing | Payment status: {enabled, card_on_file, exempt, suspended} |
| POST | /billing/setup · /billing/portal | A Stripe page URL to add a card · manage billing |
Limits
- 5 sandboxes running at once per account by default. Ask us to raise it.
- Sandbox names: 1–40 characters, lowercase letters, digits and dashes.
- Up to 100 sandboxes per
neworforkcall; up to 20 SSH keys. execoutput is capped at 1 MiB per stream; timeout up to 1 hour.- Abuse such as crypto mining, spam or network scanning gets accounts suspended.