Cómo alojar sandboxd en un VPS con Docker
Instale sandboxd en su VPS con una versión fijada, claves de modelo y HTTPS. Conozca los mínimos de RAM y disco y cómo limpiar sandboxes obsoletos.
Qué es sandboxd y qué obtiene al ejecutarlo por su cuenta
Para alojar sandboxd por su cuenta necesita un servidor Linux con Docker y un nombre de dominio. Envía una instrucción, un agente de programación crea una aplicación real dentro de un contenedor aislado y la aplicación queda disponible en su propia URL de vista previa. Los generadores de aplicaciones a partir de instrucciones son la categoría alojada más visible de 2026, y sandboxd es la opción que se ejecuta en su VPS, bajo la licencia MIT, con el código generado almacenado en su propio disco.
El diseño es pequeño de forma intencionada. Un plano de control escrito en Go controla Docker, Traefik v3 enruta cada nombre de host de vista previa, SQLite almacena el estado y cada aplicación se ejecuta dentro de un contenedor. No hay Kubernetes ni un servidor de base de datos independiente. Por eso puede ejecutarse en un equipo con 2 vCPU.
Cuatro objetos componen todo el modelo. Una app es el proyecto persistente. Contiene su nombre, sus metadatos de git y sus secretos. Un sandbox es el contenedor de Docker en el que se ejecuta la app, y una app apunta a un sandbox cada vez. Un workspace son los archivos de la app. Se almacenan en el host y sobreviven al contenedor. Una task es una instrucción entregada al agente dentro del sandbox. Detener un sandbox libera memoria y conserva los archivos. Destruirlo elimina el contenedor, y la app puede iniciar uno nuevo.
¿En qué se diferencia sandboxd de Dify y OpenHands?
Estos tres proyectos se confunden porque todos ejecutan un LLM (modelo de lenguaje grande) en el servidor, pero generan resultados diferentes. Dify crea aplicaciones basadas en LLM: interfaces de chat, canalizaciones de recuperación y flujos de trabajo que llaman a un modelo cada vez que alguien los utiliza. El modelo forma parte del producto final. OpenHands trabaja con un repositorio existente: se le indica el código, lee archivos, ejecuta comandos y propone cambios. sandboxd parte de cero. Crea la estructura de un proyecto a partir de un preset, lo compila en un contenedor nuevo y proporciona una URL para consultarlo. El resultado es una aplicación React o FastAPI normal que no necesita ningún modelo para ejecutarse.
Por tanto, debe elegir según el resultado que necesite. sandboxd sirve para empezar con una frase y conservar después el código. Los otros dos sirven cuando el repositorio o el producto basado en modelos ya existen.
La otra diferencia es la antigüedad, y es el aspecto que debe valorar antes de crear algo real sobre esta base.
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 tiene 875 estrellas, frente a 83,091 de OpenHands y 151,320 de Dify. El repositorio se creó el 3 de junio de 2026, por lo que tiene dos meses en agosto de 2026, mientras que OpenHands se remonta a marzo de 2024 y Dify a abril de 2023. La versión v0.1.0 se publicó el 6 de junio de 2026 y la v0.3.6, el 1 de agosto de 2026. El proyecto se considera beta y advierte que las versiones 0.x pueden romper la compatibilidad. Interprete esas cifras como un indicador del riesgo asociado a las dependencias, no como una evaluación de la calidad: un proyecto con dos meses de antigüedad sólo ha tenido dos meses para que otras personas encuentren sus errores.
Qué necesita el servidor y qué falla cuando escasea
El proyecto indica que 2 vCPU y 4 GB de RAM bastan para empezar. Esto es correcto para el plano de control y un sandbox pequeño, pero no basta para que dos personas compilen al mismo tiempo. Distribuya la memoria por componentes. Traefik y el plano de control escrito en Go consumen pocos recursos. Cada sandbox en ejecución contiene una cadena de herramientas completa de Node o Python, y el consumo máximo se produce durante un npm install seguido de una compilación de producción. Planifique 8 GB para un equipo que mantendrá varias aplicaciones activas y considere la swap como una red de seguridad, no como capacidad disponible, porque una compilación que usa swap tarda minutos en lugar de segundos.
Cuando se agota la memoria se producen dos fallos distintos, que no se parecen entre sí. Dentro de un sandbox, el contenedor alcanza el límite estricto de --memory que establece sandboxd y el kernel mata el proceso más grande, por lo que la compilación falla sin que el agente muestre un mensaje útil. docker ps -a muestra el código de salida 137 para ese contenedor y docker inspect sobre él informa de "OOMKilled": true. Una compilación de Node que termina de esta forma suele mostrar primero JavaScript heap out of memory.
El segundo fallo se produce en el host. sandboxd ejecuta un proceso de recuperación ante presión de memoria que detiene los sandboxes cuando queda poca memoria en el host. Por eso, en un equipo pequeño, un sandbox puede desaparecer mientras observa su vista previa. Los archivos permanecen intactos y la siguiente petición a la URL de vista previa lo reactiva, pero una tarea que se estaba ejecutando cuando se detuvo el contenedor no se reanuda.
El disco es el problema menos visible. Cada aplicación mantiene su propio espacio de trabajo en el host, y un proyecto de JavaScript contiene un árbol node_modules de cientos de megabytes. Diez aplicaciones ocupan varios gigabytes de dependencias antes de contar las imágenes. Empiece con 40 GB y vigílelo:
docker system df
sudo du -sh /var/lib/sandboxed/workspacesEl directorio de datos predeterminado es /var/lib/sandboxed, escrito con el e adicional. Si escribe /var/lib/sandboxd, obtendrá un directorio vacío y cinco minutos de confusión.
Instalar una versión fijada de sandboxd
Primero, el servidor debe tener Docker Engine con el plugin Compose, además de git. Instalar Docker en un VPS explica esa parte.
docker compose version
git --versionAmbos comandos deben mostrar una versión. docker: 'compose' is not a docker command indica que tiene el binario independiente antiguo de docker-compose, pero el instalador espera el plugin v2.
El instalador es un script de shell que se descarga a través de la red. Léalo antes de ejecutarlo y fije la versión.
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.shSANDBOXD_REF es la referencia de git que el instalador extrae en $HOME/.sandboxd/src y, de forma predeterminada, usa main. Si no la establece, la instalación incluirá todo lo que se haya fusionado esa mañana. Esto es relevante en un proyecto que publicó seis versiones sólo en julio de 2026. Fije la versión y actualícela de forma deliberada después de leer el registro de cambios.
El script clona el código fuente, compila las imágenes, inicia la pila con docker compose up -d y muestra al final la URL de la consola y un token de API. Guarde ese token en un lugar seguro. Es la credencial de una API que controla Docker como root.
curl http://127.0.0.1:9090/healthzEsto muestra ok cuando el plano de control está activo. Si no muestra nada, la pila no se inició: ejecute docker compose ps desde ~/.sandboxd/src para identificar el servicio que está detenido y, después, docker compose logs sandboxd para averiguar la causa.
Acceso a la consola en un equipo remoto
La consola se publica mediante Traefik en HTTP_PORT, que de forma predeterminada es 80, con el nombre de host http://console.localhost. Traefik enruta según el nombre de host, por lo que introducir la dirección IP del servidor en el navegador no coincide con ninguna regla y devuelve un 404. Hasta que configure un dominio real, redirija el puerto y conserve el nombre de host:
ssh -L 8080:127.0.0.1:80 you@your-vpsA continuación, abra http://console.localhost:8080 en su portátil. En Linux y macOS, cualquier nombre que termine en .localhost se resuelve en 127.0.0.1, por lo que la solicitud atraviesa el túnel con la cabecera Host correcta. Configure la contraseña de la consola en la primera visita.
Proporcione un modelo al agente
La imagen base incluye dos agentes de programación: OpenCode y Claude Code. SANDBOXD_DEFAULT_AGENT decide cuál ejecuta una tarea que no especifica ninguno y, de forma predeterminada, usa opencode. Sin ninguna clave conectada, las tareas se ejecutan con los modelos gratuitos sin clave de OpenCode Zen. Así, la primera compilación no tiene ningún coste y puede probar todo el flujo antes de gastar dinero.
Conecte su propia clave cuando necesite un modelo más potente. Las claves se envían al plano de control, nunca al sandbox: se almacenan cifradas en el directorio de datos y un proxy de credenciales las inyecta durante la comunicación. Por tanto, ni el agente ni el código que escribe pueden leerlas.
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 consola ofrece la misma configuración en Settings, AI Agents, incluido un flujo guiado de OAuth si quiere usar una suscripción de Claude en lugar de una clave de API. El modelo predeterminado de cada agente se configura en el mismo panel, y una tarea concreta puede sustituirlo.
Crear una aplicación pequeña de principio a fin
Cree la aplicación, inicie su sandbox y envíe un prompt. Los identificadores se devuelven como JSON, y el inicio rápido los extrae con sed, por lo que no necesita tener jq instalado.
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"Ambas variables deben contener un identificador. Un $SB vacío significa que el sandbox nunca se inició. La causa habitual es que la imagen base todavía se está compilando o que el host se ha quedado sin memoria. Un 401 en lugar de un identificador significa que el bearer token no es válido.
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 respuesta contiene un identificador de tarea. GET /v1/sandboxes/$SB/tasks/<task id> devuelve su resultado, y la ruta /events de la misma tarea es un flujo SSE (eventos enviados por el servidor) en tiempo real que muestra lo que hace el agente. La consola muestra el mismo flujo como un chat.
La aplicación estará disponible en http://s-<sandbox id>-3000.preview.localhost, donde 3000 es el puerto que solicitó. Si el sandbox estaba inactivo, la primera solicitud llega al catch-all de Traefik, sandboxd inicia el contenedor, espera a que el puerto responda y muestra una página breve de inicialización que se actualiza para cargar la aplicación. Una vista previa que nunca abandona esa página indica que el proceso dentro del sandbox no está escuchando en el puerto declarado en sandbox.yaml de la aplicación.
Publica las previsualizaciones en un dominio real con HTTPS
Cada entorno aislado obtiene su propio nombre de host, por lo que un único registro DNS comodín cubre todos. Apunta *.preview.yourdomain.com a la dirección IP del servidor mediante un registro A. Después, define las variables de previsualización en .env dentro de ~/.sandboxd/src:
PREVIEW_DOMAIN=yourdomain.com
PREVIEW_ENTRYPOINT=websecure
PREVIEW_TLS=true
SANDBOXD_API_AUTH_DISABLED=falseTraefik necesita la configuración correspondiente: habilita el punto de entrada websecure en traefik/traefik.yml y añade un resolvedor de certificados. Usa el desafío DNS-01, porque un único certificado comodín cubre todos los nombres de host de las previsualizaciones. Con HTTP-01, cada nuevo entorno aislado necesitaría su propia emisión, y una tarde con muchas compilaciones puede alcanzar rápidamente los límites de tasa de Let’s Encrypt. Certificados comodín mediante el desafío DNS-01 explica la configuración de DNS.
cd ~/.sandboxd/src
docker compose up -dLas URL de las previsualizaciones pasan a ser https://s-<id>-3000.preview.yourdomain.com. Abre 80 y 443 en el firewall y mantén 9090 cerrado al exterior: consulta reglas básicas del firewall ufw. Recuerda que cualquiera que pueda adivinar un nombre de host de previsualización puede cargar la aplicación, así que trátalas como públicas.
¿Dónde se guarda el código generado y se puede exportar?
En el host, dentro del directorio de datos. Cada espacio de trabajo es un directorio normal en /var/lib/sandboxed/workspaces/<id>/, montado mediante bind en el contenedor, y los archivos de la aplicación se encuentran en /home/sandbox/workspace/app dentro del sandbox. El estado del plano de control está en un único archivo SQLite en state/sandboxd.db, y las credenciales cifradas de los agentes están en agent-auth/. No se oculta nada en una capa del contenedor, por lo que una copia de seguridad consiste en copiar el directorio y ese archivo de base de datos. las copias de seguridad de restic en un VPS gestiona ambos elementos.
sudo ls /var/lib/sandboxed/workspaces
sudo du -sh /var/lib/sandboxed/workspaces/*La exportación a Git está integrada, no añadida como un componente externo. La API expone el estado y las diferencias para su consulta, y después permite hacer commit y 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 privado necesita un personal access token, que se configura una vez en la consola, en Settings, Git credentials. Se almacena cifrado y permanece fuera del sandbox, por lo que el agente no puede leerlo ni hacer push con él sin que lo sepa. Haga push pronto y con frecuencia. Hasta que lo haga, el directorio del espacio de trabajo es la única copia del código, y DELETE /v1/apps/<id> lo elimina sin posibilidad de recuperación.
¿Cuántos tokens del modelo cuesta una compilación?
sandboxd no mide su gasto, por lo que la cifra relevante se encuentra en la consola de su proveedor. Los modelos gratuitos de OpenCode Zen no tienen coste y son más lentos y menos capaces que un modelo de pago. Esto se refleja en más rondas de corrección para cualquier aplicación que supere un ejemplo básico.
La factura depende de cómo funciona el bucle del agente. Cada turno vuelve a enviar el contexto que necesita, por lo que el coste depende del número de turnos, no del número de aplicaciones. Un prompt que produce el resultado esperado es barato. Quince rondas de «ahora corrija el espaciado» sobre un proyecto con cincuenta archivos no lo son, porque el contenido de los archivos se envía cada vez. Los tokens de entrada y salida se facturan de forma diferente, y el coste de un agente de programación por sesión ofrece un intervalo realista. Establezca un límite de gasto estricto en el proveedor antes de entregar las credenciales a un bucle que se ejecuta sin supervisión.
Limpieza de sandboxes obsoletos
El proceso de limpieza por inactividad detiene cualquier sandbox que haya estado inactivo durante más de SANDBOXD_IDLE_THRESHOLD_SECONDS, cuyo valor predeterminado es 2100 segundos, o 35 minutos. Esto libera la RAM y conserva los archivos. La siguiente solicitud a la URL de vista previa vuelve a activar el contenedor. Reduzca este valor en un servidor pequeño, porque 35 minutos de contenedores inactivos son 35 minutos de memoria que no puede utilizar.
Detener no equivale a eliminar. Aquí es donde los discos se llenan silenciosamente. Un sandbox detenido todavía conserva su espacio de trabajo y su contenedor. Eliminar el sandbox pero conservar la aplicación es un DELETE del sandbox, que elimina el contenedor y el espacio de trabajo. Eliminar la aplicación elimina todo de forma permanente.
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 itDespués de varias semanas de pruebas, docker system df mostrará más espacio recuperable de imágenes del que esperaba, porque cada aplicación que descargó su propia cadena de herramientas dejó capas sin utilizar. docker image prune elimina las capas huérfanas. Compruebe primero GET /v1/apps, porque una imagen a la que todavía hace referencia un sandbox suspendido no es basura.
Qué ofrece y qué no ofrece el límite del contenedor
Cada entorno aislado se ejecuta como un usuario sin privilegios, con un sistema de archivos raíz de sólo lectura, todas las capacidades de Linux eliminadas, no-new-privileges establecido, un límite de memoria y un límite de procesos. El proyecto reconoce claramente este límite: un contenedor Linux con un kernel compartido ofrece un aislamiento sólido, pero una frontera de seguridad débil. Un error del kernel puede comprometer el host.
Hay dos aspectos que requieren medidas. La salida de red desde un entorno aislado está abierta en la compilación autohospedada, por lo que el código generado puede acceder a Internet, a la red local y a los endpoints de metadatos de la nube. Existe un subsistema de salida de nftables en el código fuente, pero está desactivado en la compilación portable de Docker Compose. Por tanto, los límites deben aplicarse mediante el firewall del host. Además, la API del plano de control equivale en la práctica a root en el host, porque controla el socket de Docker. Se enlaza a 127.0.0.1:9090 de forma predeterminada, SANDBOXD_API_AUTH_DISABLED debe permanecer en false y nunca debe publicarse en Internet.
Si planea permitir que otras personas envíen prompts a su equipo, este modelo es demasiado débil por sí solo. El proyecto recomienda gVisor con SANDBOXD_RUNTIME=runsc, que coloca un kernel en espacio de usuario entre el entorno aislado y el host y hace que las cargas con muchas llamadas al sistema sean aproximadamente entre 1.7 y 4 veces más lentas. La opción con un aislamiento mayor es usar una máquina por tenant, que sigue el mismo razonamiento que ejecutar agentes de programación en una VM desechable.
¿Conviene basarse en un proyecto con dos meses de antigüedad?
Para un servidor de compilación personal, sí, con las precauciones habituales: fije SANDBOXD_REF, haga copias de seguridad de /var/lib/sandboxed y envíe cada aplicación importante a un repositorio remoto de git. Para cualquier aplicación que utilice un cliente, espere a la versión 1.0 o reserve tiempo y recursos para resolver fallos, porque los responsables indican claramente que la versión 0.x puede cambiar sin mantener la compatibilidad. Los responsables también ofrecen una instalación administrada por 79 dólares al mes desde agosto de 2026. Conviene tenerlo en cuenta al evaluar si el proyecto tiene motivos para seguir existiendo.
El riesgo es tolerable por el resultado. sandboxd genera una aplicación normal en un repositorio normal de git. Si el desarrollo del proyecto se detiene, conserva el código y sólo pierde la capa de integración. Es una situación mucho mejor que la de un generador alojado que controla su proyecto. Para conocer una perspectiva más amplia sobre qué merece un lugar en su servidor este año, consulte qué merece la pena alojar por cuenta propia en 2026.
FAQ
¿Cuáles son las especificaciones mínimas del servidor para sandboxd?
El proyecto indica que 2 vCPU y 4 GB de RAM son suficientes para empezar. Esto cubre el plano de control, Traefik y un sandbox pequeño. Use 8 GB de RAM y 40 GB de disco si quiere mantener varias aplicaciones activas a la vez, porque cada sandbox en ejecución incluye una cadena de herramientas completa de Node o Python y cada espacio de trabajo conserva su propio árbol de dependencias en disco. Cuando el host se queda sin memoria, el recolector de presión de sandboxd detiene sandboxes para liberarla. Además, el kernel termina una compilación que supera el límite de memoria de su contenedor: docker ps -a muestra el código de salida 137.
¿En qué se diferencia sandboxd de Dify u OpenHands?
Generan artefactos distintos. Dify crea aplicaciones que llaman a un modelo durante la ejecución, como interfaces de chat y canalizaciones de recuperación. OpenHands modifica un repositorio existente, ejecuta comandos y propone cambios en el código actual. sandboxd genera un proyecto completamente nuevo a partir de un prompt, lo compila dentro de su propio contenedor y lo sirve en una URL de vista previa. El resultado es una aplicación web normal que no necesita un modelo para ejecutarse.
¿Dónde se almacena realmente el código que escribe el agente?
En el sistema de archivos del host, no dentro de una imagen de contenedor. Cada aplicación obtiene un directorio en /var/lib/sandboxed/workspaces/<id>/ que se monta mediante bind mount en su sandbox, y los archivos aparecen en /home/sandbox/workspace/app dentro de este. El estado del plano de control se almacena en un único archivo SQLite en state/, dentro del mismo directorio de datos. Puede hacer commit y push a un repositorio remoto de git desde la pestaña Git de la consola o mediante los endpoints /v1/apps/<id>/git/commit y /git/push. El plano de control almacena cifrado el token para repositorios remotos privados, en lugar de entregárselo al sandbox.
¿Es seguro exponer sandboxd a Internet?
Exponga las URL de vista previa y la consola, pero nunca la API del plano de control. Esa API controla Docker en el host, por lo que equivale a root. Por este motivo, se enlaza a 127.0.0.1:9090 de forma predeterminada. Además, los sandboxes tienen salida de red abierta en la compilación autohospedada. Esto significa que el código escrito por el agente puede acceder a la red local y a los endpoints de metadatos de la nube. Añada reglas de firewall del host si hay otros sistemas en la red que deba proteger. Para prompts de personas en las que no confía, use un host por tenant en lugar de depender del aislamiento del contenedor.