SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-13

Zelf sandboxd hosten op een VPS: handleiding

Installeer sandboxd op uw eigen VPS met Docker en Traefik. Leer hoe u model-keys configureert, HTTPS-preview-URL's instelt en schijfruimte beheert voor actieve AI-projecten.

Wat sandboxd is en wat u krijgt als u het zelf draait

Om sandboxd zelf te hosten heeft u één Linux-server met Docker en een domeinnaam nodig. U verstuurt een prompt, een coding agent bouwt een echte applicatie in een geïsoleerde container en die applicatie verschijnt op een eigen preview-URL. Prompt-to-app builders zijn de meest prominente gehoste categorie van 2026, en sandboxd is de variant die op uw eigen VPS draait, onder de MIT-licentie, waarbij de gegenereerde code op uw eigen schijf staat.

Het ontwerp is bewust compact gehouden. Een Go-control plane stuurt Docker aan, Traefik v3 routeert elke preview-hostnaam, SQLite beheert de status en elke app draait in één container. Er is geen Kubernetes en geen aparte databaseserver, waardoor een machine met 2 vCPU's dit kan draaien.

Vier objecten vormen de basis van het model. Een app is het duurzame project, dat de naam, git-metadata en secrets bevat. Een sandbox is de Docker-container waarin de app draait; een app verwijst telkens naar één sandbox. Een workspace bestaat uit de bestanden van de app, die op de host staan en blijven bestaan als de container stopt. Een task is één prompt die aan de agent in de sandbox wordt meegegeven. Het stoppen van een sandbox maakt geheugen vrij en behoudt de bestanden. Het vernietigen ervan verwijdert de container, waarna de app een nieuwe kan opstarten.

Wat is het verschil tussen sandboxd, Dify en OpenHands?

Deze drie worden vaak verward omdat ze alle drie een LLM (large language model) op uw server uitvoeren, maar het eindresultaat verschilt. Dify bouwt LLM-applicaties: chatinterfaces, retrieval-pipelines en workflows die bij elk gebruik een model aanroepen. Het model is onderdeel van het eindproduct. OpenHands werkt aan een bestaande repository: u koppelt het aan uw code, waarna het bestanden leest, commando's uitvoert en wijzigingen voorstelt. sandboxd begint vanaf nul. Het zet een project op vanuit een sjabloon, bouwt dit in een nieuwe container en geeft u een URL om het resultaat te bekijken. Wat eruit komt is een standaard React- of FastAPI-applicatie waarvoor geen model nodig is om te draaien.

Maak uw keuze op basis van wat u aan het einde wilt hebben. sandboxd is bedoeld om te beginnen met een zin en de code daarna te behouden. De andere twee zijn bedoeld voor situaties waarin de repository of het model-gebaseerde product al bestaat.

Het andere verschil is de leeftijd, en dit is een factor om te overwegen voordat u er iets belangrijks op bouwt.

ChartGitHub stars and forks, read from the GitHub API on 4 August 2026
The data behind this chart
[
  {
    "tool": "sandboxd",
    "github_stars": "875",
    "forks": "50"
  },
  {
    "tool": "OpenHands",
    "github_stars": "83,091",
    "forks": "10,711"
  },
  {
    "tool": "Dify",
    "github_stars": "151,320",
    "forks": "23,886"
  }
]

sandboxd heeft 875 sterren, tegenover 83,091 voor OpenHands en 151,320 voor Dify. De repository is aangemaakt op 3 juni 2026, wat betekent dat het project in augustus 2026 twee maanden oud is, terwijl OpenHands dateert van maart 2024 en Dify van april 2023. Release v0.1.0 verscheen op 6 juni 2026 en v0.3.6 op 1 augustus 2026. Het project noemt zichzelf bèta en geeft aan dat 0.x-releases de compatibiliteit kunnen verbreken. Beschouw deze cijfers als een risico voor afhankelijkheden in plaats van als een oordeel over de kwaliteit: een project van twee maanden oud heeft slechts twee maanden aan bugfixes door derden achter de rug.

Wat de server nodig heeft en wat er misgaat bij een tekort

Het project stelt dat 2 vCPU en 4 GB RAM voldoende zijn om te starten. Dat klopt voor het control plane plus één kleine sandbox, maar het is onvoldoende voor twee personen die tegelijkertijd bouwen. Reserveer het geheugen in delen. Traefik en het Go control plane zijn klein. Elke actieve sandbox bevat een volledige Node- of Python-toolchain, en de piek is een npm install gevolgd door een productie-build. Reken op 8 GB voor een server die enkele applicaties actief houdt en beschouw swap als een vangnet in plaats van als capaciteit, omdat een build die gebruikmaakt van swap minuten in plaats van seconden duurt.

Wanneer het geheugen opraakt, treden er twee verschillende fouten op die totaal niet op elkaar lijken. Binnen een sandbox bereikt de container het harde --memory-plafond dat sandboxd instelt en de kernel beëindigt het grootste proces, waardoor de build stopt zonder nuttige melding van de agent. docker ps -a toont exitcode 137 voor die container en docker inspect rapporteert daarop "OOMKilled": true. Een Node-build die op deze manier stopt, print vaak eerst JavaScript heap out of memory.

De tweede fout treedt op bij de host. sandboxd voert een pressure reaper uit die sandboxes stopt wanneer het geheugen van de host laag is; op een kleine server kan een sandbox dus verdwijnen terwijl u de preview bekijkt. De bestanden zijn veilig en het volgende verzoek aan de preview-URL activeert de sandbox opnieuw, maar een taak die liep op het moment dat de container stopte, wordt niet hervat.

Schijfruimte is het stillere probleem. Elke app behoudt zijn eigen werkruimte op de host en een JavaScript-project bevat een node_modules-boom van honderden megabytes. Tien apps betekenen enkele gigabytes aan dependencies nog voordat de images worden meegeteld. Begin met 40 GB en houd dit in de gaten:

docker system df
sudo du -sh /var/lib/sandboxed/workspaces

De standaard datamap is /var/lib/sandboxed, gespeld met de extra e. Het typen van /var/lib/sandboxd levert een lege map op en vijf verwarrende minuten.

Een specifieke release van sandboxd installeren

Docker Engine met de Compose-plugin en git moeten vooraf op de server aanwezig zijn. Docker installeren op een VPS behandelt dat onderdeel.

docker compose version
git --version

Beide moeten een versie retourneren. docker: 'compose' is not a docker command betekent dat u de oude zelfstandige docker-compose-binary heeft, terwijl het installatieprogramma de v2-plugin vereist.

Het installatieprogramma is een shell-script dat via het netwerk wordt opgehaald; lees het daarom door voordat u het uitvoert en zet de versie vast (pinning).

curl -fsSL https://raw.githubusercontent.com/tastyeffectco/sandboxd/v0.3.6/install.sh -o install-sandboxd.sh
less install-sandboxd.sh
SANDBOXD_REF=v0.3.6 bash install-sandboxd.sh

SANDBOXD_REF is de git-ref die het installatieprogramma uitcheckt naar $HOME/.sandboxd/src, met main als standaardwaarde. Als u dit niet instelt, krijgt u de versie die op dat moment is samengevoegd; dit is relevant voor een project dat alleen al in juli 2026 zes releases heeft uitgebracht. Pin de versie en voer upgrades bewust uit nadat u de changelog heeft gelezen.

Het script kloont de broncode, bouwt de images, start de stack met docker compose up -d en toont aan het einde de console-URL en een API-token. Sla dit token veilig op. Het is het authenticatiemiddel voor een API die Docker als root aanstuurt.

curl http://127.0.0.1:9090/healthz

Dit commando toont ok wanneer het control plane actief is. Als er niets wordt getoond, is de stack niet gestart: voer docker compose ps uit vanuit ~/.sandboxd/src om te zien welke service niet draait, en gebruik daarna docker compose logs sandboxd om de oorzaak te achterhalen.

Toegang tot de console op een externe server

De console wordt aangeboden via Traefik op HTTP_PORT, wat standaard poort 80 is, op de hostnaam http://console.localhost. Traefik routeert op basis van de hostnaam; het invoeren van het IP-adres van uw server in de browser komt met geen enkele regel overeen en resulteert in een 404-foutmelding. Zolang u nog geen echt domein heeft ingesteld, moet u de poort doorsturen en de hostnaam behouden:

ssh -L 8080:127.0.0.1:80 you@your-vps

Open vervolgens http://console.localhost:8080 op uw laptop. Op Linux en macOS wordt elke naam die eindigt op .localhost omgezet naar 127.0.0.1, waardoor het verzoek via de tunnel wordt verzonden met de juiste Host-header. Stel bij het eerste bezoek het wachtwoord voor de console in.

Geef de agent een model

Er worden twee coding agents meegeleverd in de basisimage: OpenCode en Claude Code. SANDBOXD_DEFAULT_AGENT bepaalt welke agent een taak uitvoert wanneer er geen specifieke agent wordt genoemd, waarbij de standaardinstelling opencode is. Als er helemaal geen sleutel is gekoppeld, worden taken uitgevoerd op de sleutelvrije gratis modellen van OpenCode Zen. Uw eerste build kost dus niets en u kunt de volledige cyclus testen voordat u kosten maakt.

Koppel uw eigen sleutel wanneer u een krachtiger model wilt gebruiken. Sleutels gaan naar het control plane en nooit naar de sandbox: ze worden versleuteld opgeslagen onder de data directory en via een credential proxy op de verbinding geïnjecteerd. Hierdoor kunnen noch de agent, noch de code die deze schrijft, de sleutels inzien.

export API=http://127.0.0.1:9090
export SANDBOXD_TOKEN=sk_...                       # printed by the installer
export AUTH="Authorization: Bearer $SANDBOXD_TOKEN"

curl -s -XPOST $API/v1/agents/claude-code/api-key -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"api_key":"sk-ant-..."}'

De console biedt dezelfde functionaliteit onder Settings, AI Agents, inclusief een begeleide OAuth-procedure als u een Claude-abonnement wilt gebruiken in plaats van een API-sleutel. Het standaardmodel per agent bevindt zich in hetzelfde paneel en kan per taak worden overschreven.

Bouw een kleine applicatie van begin tot eind

Maak de applicatie aan, start de sandbox en verstuur vervolgens een prompt. De id's worden als JSON geretourneerd en de quickstart haalt deze eruit met sed, waardoor u jq niet hoeft te installeren.

APP=$(curl -s -XPOST $API/v1/apps -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"name":"todo","runtime_preset":"react-vite"}' \
  | sed -E 's/.*"id":"([^"]+)".*/\1/')

SB=$(curl -s -XPOST $API/v1/apps/$APP/sandbox -H "$AUTH" \
  -H 'content-type: application/json' -d '{"ports":[3000]}' \
  | sed -E 's/.*"id":"([^"]+)".*/\1/')

echo "app=$APP sandbox=$SB"

Beide variabelen moeten een id bevatten. Een lege $SB betekent dat de sandbox niet is opgestart; de gebruikelijke oorzaak hiervan is dat de basis-image nog wordt gebouwd of dat het geheugen van de host vol is. Een 401 in plaats van een id betekent dat het bearer token onjuist is.

curl -s -XPOST $API/v1/sandboxes/$SB/tasks -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"prompt":"Add a todo list with a text input, an add button, and a delete button on each row. Keep the list in localStorage.","agent":"opencode"}'

Het antwoord bevat een task id. GET /v1/sandboxes/$SB/tasks/<task id> retourneert het resultaat en het /events-pad op dezelfde taak is een live SSE (server sent events) stream van de acties van de agent. De console toont dezelfde stream als een chat.

De applicatie is vervolgens beschikbaar op http://s-<sandbox id>-3000.preview.localhost, waarbij 3000 de poort is die u heeft opgegeven. Als de sandbox in de slaapstand stond, komt het eerste verzoek terecht bij de catch-all van Traefik; sandboxd start de container, wacht tot de poort reageert en toont een korte opstartpagina die ververst naar uw applicatie. Een preview die op die pagina blijft hangen, betekent dat het proces binnenin niet luistert op de poort die is gedeclareerd in de sandbox.yaml van de applicatie.

Plaats de previews op een echt domein met HTTPS

Elke sandbox krijgt een eigen hostnaam, waardoor één wildcard DNS-record voor alle sandboxen volstaat. Wijs *.preview.yourdomain.com naar het IP-adres van de server met een A-record. Stel vervolgens de preview-variabelen in .env in binnen ~/.sandboxd/src:

PREVIEW_DOMAIN=yourdomain.com
PREVIEW_ENTRYPOINT=websecure
PREVIEW_TLS=true
SANDBOXD_API_AUTH_DISABLED=false

Traefik heeft de bijbehorende configuratie nodig: schakel het websecure entrypoint in traefik/traefik.yml in en voeg een certificate resolver toe. Gebruik de DNS-01 challenge, omdat één wildcard-certificaat dan alle preview-hostnamen dekt. Bij HTTP-01 zou elke nieuwe sandbox een eigen uitgifte vereisen, en een drukke middag vol builds leidt direct tot de rate limits van Let's Encrypt. Wildcard-certificaten via de DNS-01 challenge behandelt de DNS-kant hiervan.

cd ~/.sandboxd/src
docker compose up -d

Preview-URL's worden https://s-<id>-3000.preview.yourdomain.com. Open poort 80 en 443 op de firewall en laat poort 9090 gesloten voor de buitenwereld: zie basis ufw firewall-regels. Houd er rekening mee dat iedereen die een preview-hostnaam kan raden de applicatie kan laden; behandel previews daarom als publiek toegankelijk.

Waar wordt de gegenereerde code opgeslagen en kan ik deze exporteren?

Op de host, onder de datamap. Elke werkruimte is een gewone map op /var/lib/sandboxed/workspaces/<id>/, die via een bind mount in de container wordt geplaatst, en de bestanden van de applicatie bevinden zich op /home/sandbox/workspace/app binnen de sandbox. De status van het control plane is één enkel SQLite-bestand op state/sandboxd.db, en versleutelde agent-inloggegevens staan in agent-auth/. Niets is verborgen in een containerlaag, dus een back-up is een kopie van de map plus dat databasebestand. restic back-ups op een VPS handelt beide af.

sudo ls /var/lib/sandboxed/workspaces
sudo du -sh /var/lib/sandboxed/workspaces/*

Git-export is ingebouwd in plaats van achteraf toegevoegd. De API stelt status en diff beschikbaar om te lezen, waarna u kunt committen en pushen:

curl -s $API/v1/apps/$APP/git/status -H "$AUTH"

curl -s -XPOST $API/v1/apps/$APP/git/commit -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"message":"todo list, first pass"}'

curl -s -XPOST $API/v1/apps/$APP/git/push -H "$AUTH" \
  -H 'content-type: application/json' -d '{"branch":"main"}'

Een private remote vereist een personal access token, dat eenmalig wordt ingesteld in de console onder Settings, Git credentials. Dit wordt versleuteld opgeslagen en blijft buiten de sandbox, zodat de agent het niet kan lezen of zonder uw medeweten kan pushen. Push vroeg en vaak. Totdat u dit doet, is de werkruimtemap de enige kopie van de code, en DELETE /v1/apps/<id> verwijdert deze zonder mogelijkheid tot herstel.

Wat kost een build aan model-tokens?

sandboxd houdt uw uitgaven niet bij; het relevante getal vindt u in de console van uw provider. De gratis OpenCode Zen-modellen kosten niets, maar zijn trager en minder krachtig dan een betaald model. Dit vertaalt zich in meer correctierondes bij alles wat complexer is dan een eenvoudige applicatie.

De hoogte van de factuur volgt de werking van een agent-loop. Elke beurt verstuurt de benodigde context opnieuw, waardoor de kosten afhangen van het aantal beurten, niet van het aantal applicaties. Eén prompt die in één keer slaagt, is goedkoop. Vijftien rondes van "pas nu de witruimte aan" voor een project met vijftig bestanden is dat niet, omdat de bestandsinhoud telkens opnieuw wordt meegestuurd. Input- en output-tokens worden verschillend geprijsd, en wat een coding agent per sessie kost geeft een realistisch bereik aan. Stel een harde bestedingslimiet in bij de provider voordat u een loop die onbeheerd draait, toegang geeft tot uw account.

Opschonen van verouderde sandboxes

De idle reaper stopt elke sandbox die langer dan SANDBOXD_IDLE_THRESHOLD_SECONDS inactief is; de standaardwaarde hiervoor is 2100 seconden, oftewel 35 minuten. Dit geeft het RAM-geheugen vrij en behoudt de bestanden; het volgende verzoek aan de preview-URL wekt de container weer. Verlaag deze waarde op een kleine server, omdat 35 minuten aan inactieve containers gelijkstaat aan 35 minuten aan geheugen dat u niet kunt gebruiken.

Stoppen is niet hetzelfde als verwijderen, en dit is de reden waarom schijven ongemerkt volraken. Een gestopte sandbox behoudt zijn workspace en zijn container. Het verwijderen van de sandbox terwijl de app behouden blijft, is een DELETE op de sandbox, waarbij zowel de container als de workspace worden verwijderd. Het verwijderen van de app verwijdert alles definitief.

curl -s -XPOST $API/v1/sandboxes/$SB/stop -H "$AUTH"     # frees RAM, keeps files
curl -s -XDELETE $API/v1/sandboxes/$SB -H "$AUTH"        # container and workspace gone
curl -s -XDELETE $API/v1/apps/$APP -H "$AUTH"            # app and everything under it

Na enkele weken experimenteren zal docker system df meer terugwinbare schijfruimte voor images tonen dan u wellicht verwacht, omdat elke app die zijn eigen toolchain heeft opgehaald, lagen heeft achtergelaten. docker image prune verwijdert de zwevende lagen. Controleer eerst GET /v1/apps, aangezien een image waarnaar nog wordt verwezen door een slapende sandbox geen afval is.

Wat de container-grens u wel en niet biedt

Elke sandbox draait als een ongeprivilegieerde gebruiker met een alleen-lezen root-bestandssysteem, waarbij alle Linux-capabilities zijn verwijderd, no-new-privileges is ingesteld, en er een geheugenlimiet en proceslimiet gelden. Het project is transparant over de beperkingen hiervan: een Linux-container met een gedeelde kernel vormt een sterke isolatiegrens, maar een zwakke beveiligingsgrens. Een kernel-bug betekent een compromittering van de host.

Twee feiten vereisen actie. Uitgaand netwerkverkeer vanuit een sandbox staat in de self-hosted build open, waardoor gegenereerde code het internet, uw lokale netwerk en cloud-metadata-eindpunten kan bereiken. Een nftables-subsysteem voor uitgaand verkeer is aanwezig in de broncode, maar is uitgeschakeld in de portable Docker Compose-build; dit betekent dat de beperkingen vanuit uw host-firewall moeten komen. De control plane API heeft in feite root-rechten op de host, omdat deze de Docker-socket aanstuurt. Deze bindt standaard aan 127.0.0.1:9090, SANDBOXD_API_AUTH_DISABLED moet false blijven, en deze mag nooit worden blootgesteld aan het internet.

Als u van plan bent anderen prompts naar uw box te laten sturen, is dit model op zichzelf te zwak. Het project wijst op gVisor met SANDBOXD_RUNTIME=runsc, wat een userspace-kernel tussen de sandbox en de host plaatst en ongeveer 1,7 tot 4 keer trager is bij syscall-intensief werk. Het sterkere antwoord is één machine per gebruiker; dit is hetzelfde argument als het draaien van coding agents in een wegwerpbare VM.

Is het verstandig om te bouwen op een project van twee maanden oud?

Voor een persoonlijke build-server wel, mits u de voor de hand liggende voorzorgsmaatregelen neemt: pin SANDBOXD_REF, maak back-ups van /var/lib/sandboxed en push elke applicatie die voor u van belang is naar een git-remote. Voor alles waar een klant mee in aanraking komt, kunt u beter wachten op versie 1.0 of budget reserveren voor eventuele defecten, aangezien de beheerders expliciet aangeven dat 0.x-versies ingrijpend kunnen veranderen. De beheerders bieden sinds augustus 2026 ook een beheerde installatie aan voor 79 dollar per maand; dit is nuttige informatie wanneer u beoordeelt of het project bestaansrecht heeft.

Het risico is acceptabel vanwege het resultaat. sandboxd genereert een standaard applicatie in een standaard git-repository. Als het project stopt, behoudt u de code en verliest u alleen de wrapper. Dat is een aanzienlijk betere positie dan bij een gehoste builder die eigenaar is van uw project. Voor een breder overzicht van wat dit jaar een plek op uw server verdient, zie wat is de moeite waard om in 2026 zelf te hosten.

FAQ

Wat zijn de minimale serverspecificaties voor sandboxd?

Het project geeft aan dat 2 vCPU en 4 GB RAM voldoende zijn om te starten; dit dekt de control plane, Traefik en één kleine sandbox. Gebruik 8 GB RAM en 40 GB schijfruimte als u meerdere applicaties tegelijkertijd wilt draaien, omdat elke actieve sandbox een volledige Node- of Python-toolchain bevat en elke workspace een eigen afhankelijkhedenboom op de schijf bijhoudt. Wanneer de host tekortkomt, stopt de pressure reaper van sandboxd sandboxes om geheugen vrij te maken. Een build die het geheugenlimiet van zijn container overschrijdt, wordt door de kernel beëindigd: docker ps -a toont hiervoor exitcode 137.

Hoe verschilt sandboxd van Dify of OpenHands?

Zij produceren verschillende artefacten. Dify bouwt applicaties die tijdens runtime een model aanroepen, zoals chatinterfaces en retrieval-pipelines. OpenHands bewerkt een repository die u al heeft, voert commando's uit en stelt wijzigingen voor aan bestaande code. sandboxd genereert een volledig nieuw project op basis van een prompt, bouwt dit in een eigen container en serveert het via een preview-URL. Het resultaat is een gewone webapplicatie die geen model nodig heeft om te draaien.

Waar bevindt de code die de agent schrijft zich daadwerkelijk?

Op het bestandssysteem van de host, niet in een container-image. Elke app krijgt een map op /var/lib/sandboxed/workspaces/<id>/ die als bind mount in de sandbox wordt gekoppeld; de bestanden verschijnen op /home/sandbox/workspace/app binnen de sandbox. De status van de control plane is een enkel SQLite-bestand onder state/ in dezelfde datamap. U kunt vanuit het Git-tabblad van de console of via de /v1/apps/<id>/git/commit en /git/push endpoints wijzigingen committen en pushen naar een git-remote. Een token voor private remotes wordt versleuteld door de control plane opgeslagen in plaats van aan de sandbox verstrekt.

Is het veilig om sandboxd bloot te stellen aan het internet?

Stel de preview-URL's en de console bloot, maar nooit de control plane API. Die API stuurt Docker aan op de host en is daarom gelijkwaardig aan root; om die reden luistert deze standaard op 127.0.0.1:9090. Sandboxes hebben in de self-hosted build ook open netwerk-egress, wat betekent dat code die de agent schrijft uw lokale netwerk en cloud-metadata-endpoints kan bereiken. Voeg daarom host-firewallregels toe als de server buren heeft die bescherming behoeven. Voor prompts van personen die u niet vertrouwt, dient u één host per tenant te draaien in plaats van te vertrouwen op de container-isolatie.

#sandboxd#ai-agents#self-hosted#app-builder#docker