123456789101112131415Developer machine | | SSH and HTTP, restricted by a GCP firewall rule v +------------------------------------------------------+ | Compute Engine VM | | | | opensandbox-server :8080 | | opensandbox-worker | | PostgreSQL + Redis | | QEMU/KVM sandbox VMs | | | | 50 GB persistent boot disk | | 375 GiB Local NVMe SSD mounted at /data | +------------------------------------------------------+
| Resource | Default |
| Machine type | n2-standard-8 |
| CPU and memory | 8 vCPU, 32 GiB |
| Operating system | Ubuntu 24.04 LTS |
| Boot disk | 50 GB pd-ssd |
| Sandbox data disk | One 375 GiB Local SSD over NVMe |
| Sandbox runtime | QEMU with nested KVM |
| Sandbox default | 2 vCPU with dynamically managed memory |
| API port | 8080 |
git, curl, jq, ssh, rsync, and openssl on your development
machine.123456export GCP_PROJECT="<your-project-id>" export GCP_ZONE="us-east4-c" gcloud auth login gcloud config set project "$GCP_PROJECT" gcloud services enable compute.googleapis.com
12345export MACHINE_TYPE="n2-standard-8" export INSTANCE_NAME="opensandbox-qemu-dev" export API_KEY="$(openssl rand -hex 32)" printf 'Save this development API key now: %s\n' "$API_KEY"
opensandbox-dev-allow. Create that rule first so the development API and SSH
are not exposed to the internet.123456789export ADMIN_CIDR="<your-public-ip>/32" gcloud compute firewall-rules create opensandbox-dev-allow \ --direction=INGRESS \ --action=ALLOW \ --rules=tcp:22,tcp:8080 \ --source-ranges="$ADMIN_CIDR" \ --target-tags=opensandbox-dev \ --description="OpenComputer development cell: SSH and API"
123gcloud compute firewall-rules update opensandbox-dev-allow \ --rules=tcp:22,tcp:8080 \ --source-ranges="$ADMIN_CIDR"
8080 restricted to trusted source
addresses. Do not expose worker port 8081, PostgreSQL, Redis, or worker RPC.1./deploy/gcp/deploy-qemu-dev.sh create
/data.12./deploy/gcp/deploy-qemu-dev.sh ssh -- \ sh -lc 'test -c /dev/kvm && echo "KVM ready" || echo "KVM missing"'
1KVM ready
1./deploy/gcp/deploy-qemu-dev.sh deploy
default.ext4: the legacy 4 GiB split-disk base.default-merged.ext4: the unified 20 GiB base used by new sandboxes./, /home/sandbox, and /workspace on one virtual
disk. The underlying image is sparse, so its logical 20 GiB size does not
consume 20 GiB of Local SSD space immediately.12./deploy/gcp/deploy-qemu-dev.sh ssh -- \ sudo journalctl -u opensandbox-server -n 100 --no-pager
1new worker registered: w-qemu-dev-1
503 no workers available, wait for registration
and retry.1234567export PUBLIC_IP="$( gcloud compute instances describe "$INSTANCE_NAME" \ --zone="$GCP_ZONE" \ --format='value(networkInterfaces[0].accessConfigs[0].natIP)' )" export OPENCOMPUTER_URL="http://${PUBLIC_IP}:8080" export OPENCOMPUTER_API_KEY="$API_KEY"
1curl -fsS "$OPENCOMPUTER_URL/health"
123456789export SANDBOX_ID="$( curl -fsS -X POST "$OPENCOMPUTER_URL/api/sandboxes" \ -H "Content-Type: application/json" \ -H "X-API-Key: $OPENCOMPUTER_API_KEY" \ -d '{"templateID":"default"}' | jq -r .sandboxID )" printf 'Sandbox: %s\n' "$SANDBOX_ID"
12345678910111213curl -fsS -X POST \ "$OPENCOMPUTER_URL/api/sandboxes/$SANDBOX_ID/exec/run" \ -H "Content-Type: application/json" \ -H "X-API-Key: $OPENCOMPUTER_API_KEY" \ -d '{ "cmd": "sh", "args": [ "-lc", "uname -a; nproc; free -h; df -h / /home/sandbox /workspace; lsblk" ], "timeout": 30 }' | jq
lsblk should show one approximately 20 GiB
vda, and all three paths should resolve to the same root filesystem.single-tenant dashboard
mode. On startup, the control plane creates or reuses one local organization
and administrator in PostgreSQL and keeps that development organization on the
Pro plan so hosted free-tier size gates do not restrict local testing. The
dashboard therefore shows the live cell's sandboxes without requiring an
external identity provider.12345678910gcloud compute ssh "$INSTANCE_NAME" \ --project="$GCP_PROJECT" \ --zone="$GCP_ZONE" \ --command=' sudo docker run --rm --network host \ -v "$HOME/opensandbox/web:/app" \ -w /app \ node:22-bookworm-slim \ sh -lc "npm ci && npm run dev -- --host 127.0.0.1" '
localhost:8080 on the
VM, so UI requests use the single-tenant identity and operate on the live
development cell.3000 on your machine:1234567gcloud compute ssh "$INSTANCE_NAME" \ --project="$GCP_PROJECT" \ --zone="$GCP_ZONE" \ -- -N \ -L 127.0.0.1:3000:127.0.0.1:3000 \ -o ExitOnForwardFailure=yes \ -o ServerAliveInterval=30
1234567export OPENSANDBOX_DASHBOARD_AUTH_MODE="single-tenant" export OPENSANDBOX_DASHBOARD_USER_EMAIL="admin@example.internal" export OPENSANDBOX_DASHBOARD_USER_NAME="Development Admin" export OPENSANDBOX_DASHBOARD_TENANT_NAME="Development" export OPENSANDBOX_DASHBOARD_TENANT_SLUG="development" ./deploy/gcp/deploy-qemu-dev.sh deploy
1./deploy/gcp/deploy-qemu-dev.sh status
12./deploy/gcp/deploy-qemu-dev.sh ssh -- \ sudo journalctl -u opensandbox-worker -f
1./deploy/gcp/deploy-qemu-dev.sh deploy
123456./deploy/gcp/deploy-qemu-dev.sh ssh -- \ sudo rm -f \ /data/firecracker/images/default.ext4 \ /data/firecracker/images/default-merged.ext4 ./deploy/gcp/deploy-qemu-dev.sh deploy
/data holds active sandbox disks and rootfs images.stop, instance deletion, and host replacement as destructive for
everything under /data. Configure object storage before testing durable
checkpoints or hibernation, and never keep the only copy of important data on
the Local SSD.1./deploy/gcp/deploy-qemu-dev.sh destroy
no workers available/dev/kvm is missing123gcloud compute instances describe "$INSTANCE_NAME" \ --zone="$GCP_ZONE" \ --format='yaml(machineType,advancedMachineFeatures)'
localhost:3000web/dist before packaging the control plane.401deploy:12curl -fsS "$OPENCOMPUTER_URL/api/sandboxes" \ -H "X-API-Key: $OPENCOMPUTER_API_KEY"
status to refresh the deployment state, update
OPENCOMPUTER_URL, and confirm that the firewall still allows your current
source address.