Hetzner + Firecracker — sandbox operations
How we run the sandbox orchestrator on dedicated hardware, how gograb-dev interacts with it, and where to look in-repo for nginx/build details. Firecracker stays on Hetzner; not on Railway.
Hetzner + Firecracker — operations
What runs where
| Piece | Where |
|---|---|
| Firecracker VMs + sandbox-orchestrator | Dedicated server (e.g. Hetzner AX), bare metal |
| TLS / reverse proxy | Nginx on the same host (see deployment/sandbox-orchestrator/nginx.conf) |
| Observe servers, metrics, destroy sandbox | gograb-dev → /infrastructure/hetzner (calls backend-ai-api) |
Production sandboxes require KVM / TAP / iptables on that host. Do not expect a generic container host to replace it.
Dual-server fleet (AX102-U vs AX42-U)
We run two dedicated hosts (see SANDBOX_SERVERS_JSON + SELF_HOSTED_INFRA_JSON in .env.local / Railway). DNS is not interchangeable — each hostname points at one machine.
| Concern | Prefer ax102-01.gograb.space (AX102-U) | Prefer ax42-01.gograb.space (AX42-U) |
|---|---|---|
Git (PLATFORM_GIT_SERVER_URL, clone URLs, /git/*) |
Yes — repos live here since S260 | No |
Primary orchestrator URL (SANDBOX_SERVER_URL fallback when not using fleet JSON) |
Yes | No |
Customer Firecracker VMs (customer-sandbox role; default Vitto / standard workspace) |
Yes (priority 1 in JSON) | Overflow when AX102 at capacity |
Executive / persistent-VM workloads (executive-sandbox role; workloadType: executive in placement router; Computer Use persistentVm; Vitto executive full-stack workspace) |
Only if AX42 full and policy allows overflow | Preferred first (lower priority in SANDBOX_SERVERS_JSON) |
| Dashboard visibility | Row from SANDBOX_SERVER_URL + key |
Second row from SELF_HOSTED_INFRA_JSON (must be distinct healthUrl from AX102) |
Git HTTP vs SANDBOX_SERVERS_JSON (authoritative)
- All git API / clone traffic (
/git/*,RemoteGitProviderService, studio universe git) usesPLATFORM_GIT_SERVER_URL+PLATFORM_GIT_API_KEYonly. In production that target is AX102-U (ax102-01.gograb.space). User role and VM placement do not change the git host. - The
git-operationsstring on a fleet entry inSANDBOX_SERVERS_JSONis not consumed bySandboxPlacementRouterfor routing (VM placement usescustomer-sandbox/executive-sandboxinWORKLOAD_ROLE_MAPonly). Treatgit-operationsas fleet metadata (documentation of which host runs the git HTTP stack)—not a second routing mechanism for git or sandboxes. - Early warning: If
git pushor dashboard “git identity” points at the wrong host, checkPLATFORM_GIT_*,proxy.conf/sandbox-apiupstream, and duplicate env keys in.env(last wins).
Code paths: SandboxPlacementRouter (libs/libs-api/sandbox-orchestration) filters by role + priority + capacity. backend-ai-api ComputerUseLoop passes workloadType from goal.persistentVm. Vitto VmSessionManager passes workloadType: 'executive' for executive full-stack workspace cold boots so placement matches ai-api behavior.
Operators: deploy orchestrator to both hosts when you change sandbox-orchestrator; use yarn sandbox:deploy:all or per-host SANDBOX_ORCHESTRATOR_SSH. Snippets: apps/gograb-dev/.../hetzner-deploy-playbook.ts.
How gograb-dev “talks” to your infrastructure
backend-ai-apireadsSANDBOX_SERVER_URL(public origin of the primary orchestrator, e.g.https://ax102-01.gograb.space) and optionallySANDBOX_SERVERS_JSONfor multi-host aggregation.- With
SANDBOX_API_KEYset onbackend-ai-api(same value as on the orchestrator), the API can callGET /sandboxes/meta/dashboard-summaryso this UI shows load, VMs, active sandboxes, Node + git identity (from the orchestrator), and can destroy a sandbox via the orchestrator.
No Hetzner Cloud token is required for that path. Optional HETZNER_API_TOKEN only adds Hetzner Cloud dedicated servers to the same table and enables cloud power actions.
Orchestrator build identity (git / Node)
GET /healthand dashboard summary include abuildobject: Node version, git SHA (from env orgit rev-parse HEADinprocess.cwd()), andprocessStartedAt(when the Node process started — useful to confirm restart, not image build time).- Env overrides (first match wins):
ORCHESTRATOR_GIT_SHA,GIT_COMMIT,RAILWAY_GIT_COMMIT_SHA,GITHUB_SHA,COMMIT_SHA. Use a systemdEnvironment=or drop-in if the deploy tree has no.git(e.g. rsync without VCS). - Prometheus: orchestrator
/metricsis linked from the expanded server row in gograb-dev. It is typically unauthenticated innginx.conf— restrict by IP or auth if the host is on a public interface.
UI: stale snapshot banner
If backend-ai-api’s list fetchedAt is older than ~2.5 minutes, gograb-dev shows an amber banner. That measures aggregation time on ai-api, not orchestrator clock skew (clocks >1 min ahead suppress the banner). Tune the interval in code if your refresh cadence changes.
Deploy script (recommended)
From the monorepo:
# On the Hetzner box (already SSH’d in, repo at ~/gspace1 or set SANDBOX_REMOTE_REPO)
yarn sandbox:deploy:host
# From your laptop (SSH key access to root@host; optional SANDBOX_REMOTE_REPO=/root/gspace1)
SANDBOX_ORCHESTRATOR_SSH=root@YOUR_HOST yarn sandbox:deploy:host -- remote
# or
yarn sandbox:deploy:host -- remote root@YOUR_HOST
--dry-run— print steps only (on your laptop, setSANDBOX_REMOTE_REPOto your local clone path if~/gspace1does not exist; on the server, default$HOME/gspace1is usually enough)--no-pull— build withoutgit pull--skip-build— onlysystemctl restart(use with care)SANDBOX_SYSTEMD_UNIT— defaults tosandbox-orchestrator— confirm withsystemctl status sandbox-orchestratoron the boxSANDBOX_RESTART_CMD— if you do not use systemd (e.g.pm2 restart …)
Full header comment: deployment/sandbox-orchestrator/deploy-orchestrator-host.sh
Does this fix dashboard-summary 404s?
Yes, when the cause is an old orchestrator binary (missing /sandboxes/meta/dashboard-summary) and you deploy current code to the same origin as SANDBOX_SERVER_URL, then restart.
No, if SANDBOX_SERVER_URL is wrong, nginx does not proxy /sandboxes/ to Node, or the failure is something else.
Day-to-day operator flow
- Monitor — Open Dedicated infrastructure in gograb-dev; use Refresh or wait for the auto refresh.
- Rebuild orchestrator after code changes — use
yarn sandbox:deploy:host(on host orremotevia SSH), or the Operations cheatsheet on that page for manual copy/paste. - Verify — From your laptop,
curl/healthand/sandboxes/meta/dashboard-summarywithX-API-Key(snippet on the same page). - Nginx — After editing
deployment/sandbox-orchestrator/nginx.confon the server,nginx -tand reload.
If dashboard metrics show 404 hints
See §3 in sandbox-orchestrator optional Railway & dashboard troubleshooting — the 404 pattern section applies to whatever host SANDBOX_SERVER_URL points at (almost always: stale orchestrator binary on Hetzner or wrong URL). That doc’s Railway sections are not part of your Firecracker path.
Related files
| File | Purpose |
|---|---|
deployment/sandbox-orchestrator/nginx.conf |
Example reverse proxy for the orchestrator host |
APIs/sandbox-orchestrator/ |
Nest app (orchestrator + dashboard-summary routes) |
APIs/backend-ai-api/.../hetzner-infra.service.ts |
Aggregates self-hosted + optional Hetzner Cloud |