SSD Nodes Learn 🎉 VPS dès $4.99/mois
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-07

Auto-héberger sandboxd, le builder IA sur un VPS

Installez sandboxd sur votre VPS avec Docker : version figée, clés de modèle, URLs HTTPS via Traefik, 2 vCPU minimum et nettoyage des sandboxes obsolètes.

Ce qu’est sandboxd et ce que vous obtenez en l’exécutant vous-même

Pour auto-héberger sandboxd, vous avez besoin d’un serveur Linux avec Docker et d’un nom de domaine. Vous envoyez un prompt, un agent de programmation construit une véritable application dans un conteneur isolé, puis cette application devient accessible à sa propre URL de prévisualisation. Les générateurs d’applications à partir de prompts constituent la catégorie hébergée la plus visible de 2026. sandboxd est celui que vous exécutez sur votre VPS, sous licence MIT, avec le code généré stocké sur votre propre disque.

La conception est volontairement réduite. Un plan de contrôle Go pilote Docker, Traefik v3 achemine chaque hostname de prévisualisation, SQLite conserve l’état et chaque application s’exécute dans un conteneur. Il n’y a ni Kubernetes ni serveur de base de données séparé. C’est ce qui permet à un serveur équipé de 2 vCPU de l’exécuter.

Quatre objets constituent l’ensemble du modèle. Une app est le projet persistant. Elle contient son nom, ses métadonnées git et ses secrets. Un sandbox est le conteneur Docker dans lequel cette app s’exécute. Une app pointe vers un seul sandbox à la fois. Un workspace correspond aux fichiers de l’app. Ils résident sur l’hôte et persistent après la suppression du conteneur. Une task est un prompt transmis à l’agent dans le sandbox. L’arrêt d’un sandbox libère la mémoire et conserve les fichiers. Sa destruction supprime le conteneur, et l’app peut en démarrer un nouveau.

En quoi sandboxd diffère-t-il de Dify et d’OpenHands ?

Ces trois outils sont souvent confondus, car ils exécutent tous un LLM (large language model) sur votre serveur, mais leur résultat est différent. Dify crée des applications LLM : interfaces de chat, pipelines de retrieval et workflows qui appellent un modèle chaque fois qu’une personne les utilise. Le modèle fait partie du produit final. OpenHands travaille sur un repository existant : vous lui indiquez votre code, puis il lit les fichiers, exécute des commandes et propose des modifications. sandboxd part de zéro. Il génère la structure d’un projet à partir d’un preset, le construit dans un conteneur neuf et vous fournit une URL à consulter. Le résultat est une application React ou FastAPI classique, qui n’a pas besoin d’un modèle pour fonctionner.

Choisissez donc l’outil selon le résultat attendu. sandboxd sert à partir d’une phrase et à conserver ensuite le code. Les deux autres servent lorsque le repository ou le produit utilisant un modèle existe déjà.

L’autre différence concerne l’ancienneté. C’est le point à évaluer avant de construire quoi que ce soit d’important dessus.

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 compte 875 stars, contre 83,091 pour OpenHands et 151,320 pour Dify. Le repository a été créé le 3 June 2026. Il a donc deux mois en August 2026, tandis qu’OpenHands date de March 2024 et Dify d’April 2023. La release v0.1.0 est sortie le 6 June 2026 et la v0.3.6 le 1 August 2026. Le projet se présente comme une beta et indique que les releases 0.x peuvent casser la compatibilité. Considérez ces chiffres comme un indicateur du risque lié aux dépendances, pas comme un jugement sur la qualité : un projet vieux de deux mois n’a eu que deux mois pour que d’autres personnes en trouvent les bugs.

Ressources nécessaires au serveur et problèmes en cas de manque

Le projet indique que 2 vCPU et 4 GB de RAM suffisent pour commencer. C’est exact pour le control plane et un petit sandbox, mais pas pour deux personnes qui effectuent des builds en même temps. Répartissez la mémoire entre les différents composants. Traefik et le control plane Go consomment peu de ressources. Chaque sandbox en cours d’exécution contient une toolchain Node ou Python complète, et le pic de consommation survient pendant un npm install suivi d’un build de production. Prévoyez 8 GB pour un serveur qui doit maintenir plusieurs applications actives. Considérez le swap comme un filet de sécurité, pas comme une capacité supplémentaire : un build qui utilise le swap prend des minutes au lieu de secondes.

Lorsque la mémoire est épuisée, deux problèmes différents peuvent survenir, et ils ne se ressemblent pas. Dans un sandbox, le conteneur atteint la limite stricte de --memory définie par sandboxd. Le kernel tue alors le processus le plus gourmand, et le build échoue sans message exploitable de la part de l’agent. docker ps -a affiche le code de sortie 137 pour ce conteneur, et docker inspect exécuté dessus signale "OOMKilled": true. Un build Node qui s’arrête de cette manière affiche souvent JavaScript heap out of memory en premier.

Le second problème se produit sur l’hôte. sandboxd exécute un pressure reaper qui arrête les sandbox lorsque la mémoire disponible sur l’hôte devient faible. Sur un petit serveur, un sandbox peut donc disparaître pendant que vous consultez sa preview. Les fichiers sont conservés, et la requête suivante vers l’URL de preview le réveille. En revanche, une tâche en cours d’exécution au moment de l’arrêt du conteneur ne reprend pas.

Le disque est un problème moins visible. Chaque application conserve son propre workspace sur l’hôte, et un projet JavaScript contient un arbre node_modules de plusieurs centaines de mégaoctets. Dix applications occupent plusieurs gigaoctets de dépendances, sans compter les images. Commencez avec 40 GB et surveillez son utilisation :

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

Le répertoire de données par défaut est /var/lib/sandboxed, avec le e supplémentaire. Si vous saisissez /var/lib/sandboxd, vous obtenez un répertoire vide et perdez cinq minutes à chercher la cause.

Installer une version figée de sandboxd

Docker Engine avec le plugin Compose, ainsi que git, doivent d’abord être installés sur le serveur. Installer Docker sur un VPS couvre cette partie.

docker compose version
git --version

Les deux commandes doivent afficher une version. docker: 'compose' is not a docker command indique que vous utilisez l’ancien binaire docker-compose autonome, alors que l’installateur attend le plugin v2.

L’installateur est un script shell récupéré sur le réseau. Lisez-le avant de l’exécuter et figez la version.

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 est la référence git que l’installateur récupère dans $HOME/.sandboxd/src. Sa valeur par défaut est main. Si vous ne la définissez pas, votre installation correspondra à l’état du projet au moment de l’exécution. C’est important pour un projet qui a publié six releases au cours du seul mois de juillet 2026. Figez la version, puis mettez-la à niveau volontairement après avoir lu le changelog.

Le script clone le code source, construit les images, démarre la stack avec docker compose up -d, puis affiche l’URL de la console et un token d’API. Enregistrez ce token dans un emplacement sûr. Il sert d’identifiant pour une API capable de piloter Docker avec les privilèges de root.

curl http://127.0.0.1:9090/healthz

Cette commande affiche ok lorsque le control plane est opérationnel. Si elle n’affiche rien, la stack n’a pas démarré : exécutez docker compose ps depuis ~/.sandboxd/src pour identifier le service arrêté, puis docker compose logs sandboxd pour en connaître la cause.

Accéder à la console sur un serveur distant

La console est servie par Traefik sur HTTP_PORT, qui correspond à 80 par défaut, avec le nom d’hôte http://console.localhost. Traefik effectue le routage selon le nom d’hôte. Si vous saisissez l’adresse IP de votre serveur dans le navigateur, aucune règle ne correspond et le serveur renvoie une erreur 404. Tant que vous n’avez pas configuré de domaine réel, transférez le port et conservez le nom d’hôte :

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

Ouvrez ensuite http://console.localhost:8080 sur votre ordinateur portable. Sous Linux et macOS, tout nom se terminant par .localhost est résolu vers 127.0.0.1. La requête passe donc par le tunnel avec le bon en-tête Host. Définissez le mot de passe de la console lors de votre première visite.

Donnez un modèle à l’agent

Deux agents de codage sont inclus dans l’image de base : OpenCode et Claude Code. SANDBOXD_DEFAULT_AGENT détermine lequel exécute une tâche qui n’en désigne aucun et utilise opencode par défaut. Si aucune clé n’est connectée, les tâches utilisent les modèles gratuits sans clé d’OpenCode Zen. Votre première compilation ne coûte donc rien et vous pouvez tester l’ensemble du processus avant toute dépense.

Connectez votre propre clé lorsque vous avez besoin d’un modèle plus performant. Les clés sont transmises au control plane, jamais au sandbox : elles sont stockées sous forme chiffrée dans le répertoire de données et injectées sur le réseau par un credential proxy. Ni l’agent ni le code qu’il écrit ne peuvent donc les lire.

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-..."}'

La console permet de faire la même chose dans Settings, AI Agents. Elle propose notamment un flux OAuth guidé si vous voulez utiliser un abonnement Claude plutôt qu’une clé d’API. Le modèle par défaut de chaque agent se configure dans ce même panneau, et une tâche peut le remplacer.

Créer une petite application de bout en bout

Créez l’application, démarrez sa sandbox, puis envoyez un prompt. Les identifiants sont renvoyés au format JSON, et le quickstart les extrait avec sed. Vous n’avez donc pas besoin d’installer jq.

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"

Les deux variables doivent contenir un identifiant. Une valeur $SB vide signifie que la sandbox n’a jamais démarré. La cause habituelle est que l’image de base est encore en cours de build ou que l’hôte manque de mémoire. Une valeur 401 à la place d’un identifiant signifie que le bearer token est incorrect.

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"}'

La réponse contient un identifiant de tâche. GET /v1/sandboxes/$SB/tasks/<task id> renvoie son résultat, et le chemin /events sur la même tâche est un flux SSE (server sent events) en temps réel qui décrit les actions de l’agent. La console affiche le même flux sous forme de chat.

L’application est ensuite accessible à l’adresse http://s-<sandbox id>-3000.preview.localhost, où 3000 correspond au port demandé. Si la sandbox était en veille, la première requête arrive sur le catch-all de Traefik. sandboxd démarre alors le conteneur, attend que le port réponde et sert une courte page de démarrage qui s’actualise pour afficher votre application. Un aperçu qui reste sur cette page signifie que le processus dans le conteneur n’écoute pas sur le port déclaré dans le sandbox.yaml de l’application.

Déployez les previews sur un vrai domaine avec HTTPS

Chaque sandbox possède son propre hostname. Un seul enregistrement DNS wildcard couvre donc toutes les sandboxes. Faites pointer *.preview.yourdomain.com vers l’adresse IP du serveur avec un enregistrement A. Définissez ensuite les variables des previews dans .env, dans ~/.sandboxd/src :

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

Traefik doit être configuré en conséquence : activez l’entrypoint websecure dans traefik/traefik.yml et ajoutez un certificate resolver. Utilisez le challenge DNS-01, car un seul certificat wildcard couvre alors tous les hostnames des previews. Avec HTTP-01, chaque nouvelle sandbox nécessiterait sa propre émission de certificat. Une après-midi chargée de builds peut alors atteindre rapidement les limites de débit de Let’s Encrypt. La section Certificats wildcard avec le challenge DNS-01 couvre la configuration DNS.

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

Les URL des previews deviennent https://s-<id>-3000.preview.yourdomain.com. Ouvrez les ports 80 et 443 dans le firewall et laissez le port 9090 fermé au monde extérieur : consultez Règles de base du firewall ufw. N’oubliez pas que toute personne capable de deviner un hostname de preview peut charger l’application. Traitez donc les previews comme des ressources publiques.

Où le code généré est-il enregistré, et pouvez-vous l’exporter ?

Sur l’hôte, dans le répertoire de données. Chaque workspace est un répertoire standard situé à /var/lib/sandboxed/workspaces/<id>/, monté dans le conteneur avec un bind mount. Les fichiers de l’application se trouvent à /home/sandbox/workspace/app dans le sandbox. L’état du control plane est stocké dans un seul fichier SQLite à state/sandboxd.db, et les identifiants chiffrés de l’agent se trouvent à agent-auth/. Rien n’est caché dans une couche de conteneur. Une sauvegarde consiste donc à copier le répertoire et ce fichier de base de données. les sauvegardes restic sur un VPS prend en charge les deux.

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

L’export Git est intégré, et non ajouté après coup. L’API permet de consulter le status et le diff, puis d’effectuer un commit et un push :

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"}'

Un remote privé nécessite un personal access token, à définir une seule fois dans la console, sous Settings, Git credentials. Il est stocké chiffré et reste en dehors du sandbox. L’agent ne peut donc pas le lire ni effectuer un push à votre insu. Effectuez des push régulièrement. Tant que vous ne l’avez pas fait, le répertoire du workspace est la seule copie du code, et DELETE /v1/apps/<id> le supprime définitivement.

Quel est le coût d’un build en tokens de modèle ?

sandboxd ne mesure pas vos dépenses. Le chiffre à retenir se trouve dans la console de votre fournisseur. Les modèles gratuits d’OpenCode Zen ne coûtent rien. Ils sont plus lents et moins performants qu’un modèle payant. Cela se traduit par davantage de cycles de correction dès que vous dépassez le stade de l’application de test.

Le montant dépend du fonctionnement de la boucle de l’agent. À chaque tour, l’agent renvoie le contexte dont il a besoin. Le coût dépend donc du nombre de tours, pas du nombre d’applications. Une seule invite correctement exécutée coûte peu. Quinze cycles de « corrigez maintenant l’espacement » sur un projet de cinquante fichiers coûtent davantage, car le contenu des fichiers est renvoyé à chaque fois. Les tokens d’entrée et de sortie n’ont pas le même tarif, et le coût d’un agent de programmation par session donne une fourchette réaliste. Définissez une limite stricte de dépenses chez le fournisseur avant de laisser une boucle s’exécuter sans surveillance.

Nettoyage des environnements isolés obsolètes

Le mécanisme de nettoyage des environnements inactifs arrête tout sandbox resté inactif au-delà de SANDBOXD_IDLE_THRESHOLD_SECONDS, dont la valeur par défaut est de 2100 secondes, soit 35 minutes. La mémoire est ainsi libérée, mais les fichiers sont conservés, et la prochaine requête vers l’URL de prévisualisation réveille le conteneur. Réduisez cette valeur sur une petite machine, car des conteneurs inactifs pendant 35 minutes mobilisent 35 minutes de mémoire que vous ne pouvez pas utiliser.

L’arrêt ne supprime rien. C’est ainsi que les disques se remplissent discrètement. Un sandbox arrêté conserve son espace de travail et son conteneur. Supprimer le sandbox tout en conservant l’application revient à effectuer un DELETE sur le sandbox : le conteneur et l’espace de travail sont alors supprimés. La suppression de l’application supprime tout, définitivement.

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

Après quelques semaines d’expérimentation, docker system df affichera plus d’espace récupérable dans les images que prévu, car chaque application qui a téléchargé sa propre toolchain a laissé des layers. docker image prune supprime les layers orphelins. Vérifiez d’abord GET /v1/apps, car une image encore référencée par un sandbox en veille n’est pas considérée comme inutile.

Ce que la frontière du conteneur garantit, et ce qu’elle ne garantit pas

Chaque sandbox s’exécute avec un utilisateur non privilégié, un système de fichiers racine en lecture seule, toutes les capabilities Linux supprimées, no-new-privileges activé, une limite de mémoire et une limite de processus. Le projet indique clairement ses limites : un conteneur Linux qui partage le kernel constitue une frontière d’isolation forte, mais une frontière de sécurité faible. Une vulnérabilité du kernel peut compromettre l’hôte.

Deux points nécessitent une action. Dans la version auto-hébergée, les connexions sortantes réseau depuis un sandbox sont ouvertes. Le code généré peut donc accéder à Internet, à votre réseau local et aux endpoints de métadonnées cloud. Un sous-système nftables pour les connexions sortantes existe dans le code source, mais il est désactivé lors de la compilation de la version Docker Compose portable. Les limites doivent donc être appliquées par le firewall de l’hôte. Par ailleurs, l’API du control plane dispose de fait des privilèges root sur l’hôte, car elle pilote le socket Docker. Elle écoute par défaut sur 127.0.0.1:9090, SANDBOXD_API_AUTH_DISABLED doit rester false, et elle ne doit jamais être publiée sur Internet.

Si vous prévoyez de laisser d’autres personnes envoyer des prompts à votre machine, ce modèle est insuffisant à lui seul. Le projet recommande gVisor avec SANDBOXD_RUNTIME=runsc. Cette solution place un kernel en espace utilisateur entre le sandbox et l’hôte et ralentit d’environ 1.7 à 4 fois les tâches qui effectuent beaucoup d’appels système. La solution la plus robuste consiste à utiliser une machine par tenant. C’est le même principe que exécuter des agents de programmation dans une VM jetable.

Faut-il utiliser un projet vieux de deux mois ?

Pour un serveur de développement personnel, oui, avec les précautions habituelles : verrouillez SANDBOXD_REF, sauvegardez /var/lib/sandboxed et envoyez chaque application importante vers un dépôt git distant. Pour toute application utilisée par un client, attendez la version 1.0 ou prévoyez des régressions, car les mainteneurs indiquent clairement qu’une version 0.x peut changer sans préavis. Les mainteneurs proposent également une installation gérée à 79 dollars par mois en août 2026. Cet élément est utile pour évaluer les raisons qui incitent le projet à continuer d’exister.

Le risque reste acceptable en raison du résultat obtenu. sandboxd produit une application classique dans un dépôt git classique. Si le développement du projet s’arrête, vous conservez donc le code et ne perdez que le wrapper. C’est une situation bien plus favorable qu’avec un builder hébergé qui contrôle votre projet. Pour avoir une vue d’ensemble des services qui méritent une place sur votre serveur cette année, consultez ce qui mérite d’être auto-hébergé en 2026.

FAQ

Quelles sont les spécifications minimales du serveur pour sandboxd ?

Le projet indique que 2 vCPU et 4 GB de RAM suffisent pour démarrer. Cela couvre le control plane, Traefik et un petit sandbox. Utilisez 8 GB de RAM et 40 GB de disque si vous voulez faire fonctionner plusieurs applications simultanément, car chaque sandbox en cours d’exécution contient une toolchain Node ou Python complète et chaque workspace conserve son propre arbre de dépendances sur le disque. Lorsque les ressources de l’hôte deviennent insuffisantes, le pressure reaper de sandboxd arrête des sandboxes pour libérer de la mémoire. Un build qui dépasse la limite mémoire de son conteneur est tué par le kernel : docker ps -a affiche alors le code de sortie 137.

Quelle est la différence entre sandboxd, Dify et OpenHands ?

Ils produisent des artefacts différents. Dify crée des applications qui appellent un modèle à l’exécution, comme des interfaces de chat et des pipelines de retrieval. OpenHands modifie un repository existant en exécutant des commandes et en proposant des changements dans le code déjà présent. sandboxd génère un projet entièrement nouveau à partir d’un prompt, le build dans son propre conteneur et le sert à une URL de preview. Le résultat est une application web ordinaire qui n’a pas besoin d’un modèle pour fonctionner.

Où se trouve réellement le code écrit par l’agent ?

Sur le système de fichiers de l’hôte, et non dans une image de conteneur. Chaque application reçoit un répertoire à /var/lib/sandboxed/workspaces/<id>/, monté avec bind mount dans son sandbox. Les fichiers apparaissent à /home/sandbox/workspace/app à l’intérieur de celui-ci. L’état du control plane est stocké dans un seul fichier SQLite sous state/, dans le même répertoire de données. Vous pouvez effectuer un commit et un push vers un dépôt git distant depuis l’onglet Git de la console, ou via les endpoints /v1/apps/<id>/git/commit et /git/push. Un token destiné aux dépôts privés est chiffré par le control plane, plutôt que transmis au sandbox.

Est-il sûr d’exposer sandboxd sur Internet ?

Exposez les URL de preview et la console, mais jamais l’API du control plane. Cette API pilote Docker sur l’hôte. Elle équivaut donc à un accès root et écoute par défaut sur 127.0.0.1:9090 pour cette raison. Les sandboxes disposent également d’un accès réseau sortant ouvert dans le build auto-hébergé. Le code écrit par l’agent peut donc atteindre votre réseau local et les endpoints de métadonnées cloud. Ajoutez des règles au firewall de l’hôte si la machine communique avec d’autres systèmes à protéger. Pour les prompts provenant de personnes auxquelles vous ne faites pas confiance, utilisez un hôte par tenant au lieu de vous fier à la séparation fournie par le conteneur.

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