Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Set up a Sandbox server

This guide deploys one controller, one Docker worker, and PostgreSQL on a dedicated Linux host. It is the smallest durable self-hosted topology for personal, team, or otherwise trusted workloads.
Docker workers are not a hostile multi-tenant VM boundary. Use the external runtime adapter with a VMM-grade driver when the host must isolate mutually hostile users.

Requirements

Prepare a dedicated Linux machine before starting. You need:
  • A recent Linux distribution with a dedicated host or VM.
  • Docker Engine and Docker Compose v2.
  • Git, OpenSSL, and at least 4 GiB of available memory.
  • A non-root operator with Docker access, or a root shell for installation.
  • A domain only when clients must connect from another machine.

1. Fetch and pin the source

Deploy a reviewed commit, not a moving checkout. Clone main, then record the exact revision in your deployment notes before upgrading it later.
git clone https://github.com/bas3line/sandbox.git cd sandbox git checkout main git rev-parse HEAD

2. Create separate runtime secrets

Keep runtime credentials outside the repository. Generate distinct API, worker, and database values; the worker token must never be reused as the API token.
sudo install -d -m 0700 /etc/sandbox sudo install -m 0600 /dev/null /etc/sandbox/runtime.env api_token=$(openssl rand -hex 32) node_token=$(openssl rand -hex 32) database_password=$(openssl rand -hex 32) sudo sh -c "printf '%s\n' \ 'SANDBOX_API_TOKEN=$api_token' \ 'SANDBOX_NODE_TOKEN=$node_token' \ 'POSTGRES_PASSWORD=$database_password' \ 'SANDBOX_PORT=127.0.0.1:8080' \ > /etc/sandbox/runtime.env" unset api_token node_token database_password

3. Start the core stack

The core Compose file keeps the controller on loopback. PostgreSQL stays on the private Compose network, and the worker connects to the controller there.
sudo docker compose \ --env-file /etc/sandbox/runtime.env \ -f deploy/compose/compose.yaml \ up --build -d

4. Verify health before clients connect

Require healthy controller, PostgreSQL, and worker processes. Do not move on because containers merely started; ask the controller itself to report its health and tunnel configuration.
sudo docker compose \ --env-file /etc/sandbox/runtime.env \ -f deploy/compose/compose.yaml \ ps sudo docker compose \ --env-file /etc/sandbox/runtime.env \ -f deploy/compose/compose.yaml \ exec -T controller sh -lc \ 'SANDBOX_URL=http://127.0.0.1:8080 SANDBOX_TOKEN="$SANDBOX__SERVER__API_TOKEN" sandbox doctor'

5. Run a disposable lifecycle test

A real smoke test creates, waits, executes, and deletes. Install the CLI on the operator machine or run it inside the controller container; configure its URL and token through your own secret mechanism first.
sandbox create --tenant smoke --image alpine:3.22 --ttl 300 --network deny sandbox wait OPERATION_ID --timeout 120 sandbox exec SANDBOX_ID -- uname -a sandbox delete SANDBOX_ID --wait
Save the returned IDs from your own deployment. OPERATION_ID and SANDBOX_ID are placeholders, not literal input.

6. Connect remote clients safely

Never expose port 8080 straight to the internet. Put the controller behind an HTTPS reverse proxy or an outbound connector, then continue with client setup. For wildcard service URLs and Cloudflare Full (strict), use custom public domains.

Operations checklist

Treat the host as production infrastructure. Back up PostgreSQL and test restores; patch Docker, the host kernel, and pinned images; monitor controller health, worker heartbeats, disk pressure, and TTL cleanup. Keep worker hosts dedicated to workloads with the same trust boundary and delete disposable environments explicitly—TTL is a backstop, not a cleanup policy.
Continue with security and isolation and operations for the full operating model. This guide is maintained from the upstream server setup document.
Protect /etc/sandbox/runtime.env. Do not commit it, paste it into an issue, add it to an agent task, or send it through ordinary chat. Keep it mode 0600 and rotate values if its boundary is ever in doubt.