Cambiar tema

Dev Sandbox autohospedado con Docker y Go: preview URLs sin Kubernetes

Easton editorial illustration: code bundle entering an isolated Docker sandbox and exiting through a guarded preview-URL portal

"El README de sandboxed describe el plano de control en Go, Docker, Traefik, SQLite, preview URLs, idle stop y límites de hardening en producción."

"La documentación de Docker sobre restricciones de recursos explica que los containers no tienen límites de CPU o memoria sin configuración explícita."

"La documentación de Docker Sandboxes usa microVMs, Docker daemon independiente, aislamiento de red y aislamiento de credenciales como modelo de seguridad más fuerte."

"El Docker provider de Traefik puede descubrir configuración de routing desde Docker labels y enrutar con Host rules a servicios en containers."

Abrir un entorno de preview por cada PR suele resolverse con Vercel o Netlify. Si importan más el costo, los datos dentro de una red privada o controlar la infraestructura, un host Docker único más un plano de control en Go puede cubrir la misma necesidad inicial. Cada sandbox tiene su propia preview URL, los recursos tienen límites y la frontera de seguridad queda explícita. Sin Kubernetes y sin varios nodos.

Tabla de decisión: cuándo usar cada opción

Cuando aparece la necesidad de “entornos de preview autohospedados”, mucha gente piensa primero en scripts con varios docker run, o salta directo a Kubernetes. Mejor empieza por esta tabla.

EscenarioOpción recomendadaMotivo
Equipo interno con menos de 10 previews concurrentes y frontera de confianza dentro del equipoDocker en un solo host + plano de control en GoLa densidad de recursos es manejable, la arquitectura es simple y no operas un cluster K8s
Equipo interno con más de 20 previews concurrentes o necesidad de alta disponibilidad multinodoK8s + aislamiento por NamespaceUn host no alcanza; necesitas scheduling entre nodos y despliegues controlados
Usuarios externos o ejecución de código no fiable, incluidos agentes autónomosmicroVMs como Docker Sandboxes o FirecrackerDocker socket equivale a mucho poder sobre el host; no debe mezclarse con workloads no fiables
Preview estática simple sin persistenciaScript shell + puerto aleatorioPuede funcionar, pero la preview URL no es estable y no hay límites sólidos de recursos ni seguridad

Usa tres criterios.

Tamaño del equipo: un host único funciona mejor para unas 10 previews internas concurrentes. Una estimación útil: 512 MB de RAM y 0,5 CPU por sandbox. Un host con 16 GB puede sostener unas 20 sandboxes antes de quedarse sin margen. Por encima de esa densidad, pasa a scheduling con K8s o reparte capacidad con microVMs.

Frontera de confianza: miembros internos y usuarios de confianza pueden correr dentro de Docker containers. Si desconocidos externos o agentes autónomos ejecutan código arbitrario, el diseño con Docker socket no es lo bastante seguro. Docker Sandboxes usa aislamiento por microVM; cada sandbox obtiene su propio Docker daemon, filesystem y red.

Camino de upgrade: empieza con Docker en un solo host para validar el loop de producto y la densidad de recursos. Si la concurrencia supera ese host o necesitas alta disponibilidad multinodo, pasa a Kubernetes. Si la frontera de confianza cambia de “equipo interno” a “usuarios externos”, pasa a microVMs.

El README de tastyeffectco/sandboxes deja clara la forma objetivo: AI app builders, agent platforms y coding playgrounds en Docker single-host. No es aislamiento de nivel microVM; es un plano de control en Go que crea containers en un host Docker único y expone preview URLs.

Desglose de arquitectura: componentes centrales del plano de control

Un entorno de preview con Docker single-host no es solo un docker run repetido. Necesita un plano de control que gestione ciclo de vida, registro de rutas y limpieza de recursos. La arquitectura de tastyeffectco/sandboxes se divide en seis módulos.

Plano de control en Go (sandboxd): corre dentro de un container y monta el Docker socket del host junto con un directorio de datos. Gestiona el ciclo de vida de sandbox containers mediante Docker CLI: crear, arrancar, detener y borrar. Todas las metadatos de sandbox viven en SQLite como source of truth.

Montaje de Docker socket: es la entrada del plano de control al Docker daemon. Al montar /var/run/docker.sock, sandboxd obtiene permisos para crear y gestionar containers. También es la frontera de privilegios de toda la arquitectura: con el Docker socket, el plano de control tiene permisos muy altos sobre el host.

Registro con Traefik labels: cuando arranca un sandbox container, el plano de control inyecta la configuración de routing de Traefik mediante Docker labels. Traefik actúa como reverse proxy, descubre rutas desde los labels y envía tráfico de *.preview.example.com al container correcto.

Almacenamiento de metadatos en SQLite: cada sandbox tiene un ID único y un directorio asociado. Las metadatos se guardan en SQLite. Los workspaces viven bajo SANDBOXED_DATA_DIR/workspaces/, con un subdirectorio por sandbox para código fuente, configuración y artefactos.

Idle reaper y pressure reaper: el idle reaper revisa cuánto tiempo lleva inactivo un sandbox container y lo detiene al superar el umbral para liberar RAM. El pressure reaper observa la presión de memoria del host y detiene algunas sandboxes antes de que el host llegue a OOM. Estos dos reapers son el núcleo de recuperación de recursos.

Wake path: después de que el idle reaper detiene un sandbox container, la primera visita a la preview URL debe despertarlo. Un catch-all de Traefik envía la request al plano de control; este arranca el container y devuelve una warming page hasta que la app esté lista.

La versión mínima útil es: container de plano de control en Go, montaje de Docker socket, Traefik, SQLite e idle reaper. El quick start local requiere Docker Engine y el plugin de Compose.

Implementación de preview URL

Lo importante de una preview URL es un dominio estable, no un puerto aleatorio. Cada sandbox recibe un host independiente {sandbox_id}.preview.example.com.

Configuración del Docker provider de Traefik

Traefik puede descubrir la configuración de routing desde labels de containers mediante el Docker provider. Configuración mínima:

# traefik.yml
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

exposedByDefault: false significa que Traefik solo descubrirá containers que lo indiquen explícitamente con labels.

Host rule y Docker labels

El plano de control inyecta labels al crear un sandbox container. Ejemplo:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.sandbox123.rule=Host(`sandbox123.preview.example.com`)"
  - "traefik.http.routers.sandbox123.entrypoints=websecure"
  - "traefik.http.services.sandbox123.loadbalancer.server.port=3000"

La Host rule envía las requests de sandbox123.preview.example.com a ese container. loadbalancer.server.port indica el puerto interno donde escucha la aplicación.

Ruta wake-on-request

Después de que el idle reaper detiene el sandbox container, la primera visita a la preview URL activa el wake flow:

  1. DNS resuelve hacia Traefik; necesitas wildcard DNS como *.preview.example.com
  2. Traefik conoce la regla de ruta de la sandbox, pero el container está detenido
  3. El catch-all de Traefik reenvía la request al wake handler del plano de control
  4. El plano de control arranca el sandbox container y devuelve una warming page
  5. Cuando el container está listo, Traefik envía las requests posteriores directamente al container

El catch-all debe tener prioridad inferior a todos los routers de sandbox:

labels:
  - "traefik.http.routers.catch-all.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.preview.example.com`)"
  - "traefik.http.routers.catch-all.priority=1"
  - "traefik.http.routers.catch-all.service=wake-service"

Toda request que no coincida con una ruta viva de sandbox cae en el catch-all, y el plano de control ejecuta la lógica de wake.

Frontera de seguridad: permisos del Docker socket

Montar el Docker socket da al plano de control poder de nivel host. Ese es el tradeoff de seguridad base de esta arquitectura: sirve para equipos internos y usuarios de confianza, no para ejecución de código no fiable.

Riesgos del Docker socket

La documentación oficial de Docker advierte que el daemon tiene superficie de ataque. Si la API de Docker se expone de forma insegura, un usuario remoto sin root puede obtener acceso root al host. Un container con /var/run/docker.sock montado puede usar Docker CLI para crear, modificar y borrar containers. También puede alcanzar filesystem y red del host mediante containers que cree.

Eso implica:

  • El container del plano de control tiene permisos muy altos sobre el host
  • No mezcles el plano de control con workloads no fiables en el mismo host
  • El código dentro de sandbox containers puede afectar indirectamente al host si puede influir en el plano de control

Frontera para escenarios no fiables

Docker single-host + plano de control en Go encaja con:

  • Entornos de preview para miembros de un equipo interno
  • Coding playgrounds para usuarios de confianza
  • Entornos internos de validación para AI app builders o agent platforms

No encaja con:

  • Ejecución de código arbitrario de usuarios externos desconocidos
  • Ejecución en producción de autonomous agents cuando el host no es de confianza
  • Plataformas multi-tenant que exigen aislamiento fuerte

Si la frontera de confianza pasa de “equipo interno” a “usuarios externos”, migra a Docker Sandboxes oficiales o a microVMs tipo Firecracker. Docker Sandboxes incluye aislamiento por hypervisor, red independiente, Docker daemon independiente, filesystem independiente y aislamiento de credenciales. Cada sandbox es una microVM completa, no un container que comparte el Docker daemon del host.

Checklist de hardening para producción

Antes de producción, añade estas fronteras:

  • Aislamiento de red: plano de control y sandbox containers corren en redes dedicadas, no en la red de negocio
  • Autenticación de API: el quick start local puede venir sin autenticación; producción necesita token u otro mecanismo de auth
  • Exposición mínima: expón preview URLs mediante Traefik, no el puerto de la API Docker
  • TLS: las preview URLs deben usar certificado TLS wildcard, no HTTP en claro
  • Logs y monitoring: registra requests de API del plano de control y eventos de ciclo de vida de containers, y genera alertas

Para fronteras más fuertes, compara gVisor, Firecracker y Kubernetes en un diseño de sandbox para AI agents.

Límites de recursos: memory / CPU / PIDs

Los containers Docker no tienen restricciones de recursos por defecto. Sin límites, un sandbox container puede consumir RAM y CPU del host y afectar a otras sandboxes y procesos del host. Un entorno de preview multi-tenant necesita límites duros por container.

Comportamiento por defecto de Docker

La documentación de Docker explica que los containers pueden usar recursos del host según lo permita el scheduler del kernel. Si no configuras --memory o --cpus, un container puede tomar recursos disponibles del host.

Límite duro de memoria

--memory define el máximo de memoria disponible para el container. Ejemplo:

docker run --memory="512m" --memory-swap="512m" sandbox-image

--memory-swap define el límite combinado de memoria + swap. Si --memory-swap es igual a --memory, el container no usa swap.

Si el container supera su límite de memoria, el OOM killer puede terminar procesos del container. Un OOM a nivel host también puede afectar a otros containers y procesos del host.

Límite de cuota de CPU

--cpus define cuánta capacidad de CPU puede usar un container. Ejemplo:

docker run --cpus="0.5" sandbox-image

El container puede usar como máximo 0,5 CPU. Con muchas sandboxes concurrentes, el límite de CPU evita que un container tome toda la capacidad de cómputo.

Límite de número de procesos

--pids-limit ayuda a evitar fork bombs. Ejemplo:

docker run --pids-limit=100 sandbox-image

El container puede crear como máximo 100 procesos. Después de eso, fork() falla.

Ejemplo de configuración con Compose

Con Docker Compose, configura límites bajo deploy.resources.limits:

services:
  sandbox:
    image: sandbox-image
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
          pids: 100

deploy.resources en Compose aplica en modo Docker Swarm. En Docker de un solo host, pasa --memory, --cpus y --pids-limit manualmente, o ejecuta docker-compose --compatibility.

El plano de control debería inyectar estos parámetros al crear sandbox containers, no depender de que los usuarios los configuren a mano.

Operación: caché de imágenes y rate limits de Docker Hub

Cuando muchas sandboxes se crean y destruyen con frecuencia, los image pulls se vuelven un cuello de botella. Docker Hub tiene pull rate limits y abuse rate limits, con políticas que cambian según tipo de cuenta y plan. Producción no debería depender de tirar cada imagen de sandbox desde Docker Hub público cada vez.

Límites de Docker Hub

La documentación de Docker explica que usuarios anónimos, usuarios autenticados y cuentas de equipo tienen políticas distintas de pull rate. Al superar el límite, las requests de pull se rechazan. Los números concretos cambian con la política; revisa Docker Hub usage and limits en vez de fijarlos en el texto.

En entornos multi-sandbox, esto importa porque:

  • Crear sandboxes con frecuencia puede tirar una imagen por cada inicio
  • Reiniciar una sandbox tras idle cleanup puede volver a necesitar la imagen
  • Varias sandboxes pueden tirar repetidamente la misma imagen

Precalentamiento de imágenes y estrategia de caché

Producción necesita varias medidas.

Precalentamiento de imágenes: tira imágenes comunes al host antes de arrancar el plano de control. Reduce la espera cuando inicia una sandbox.

Registry privado: sube imágenes comunes a un registry privado como Harbor, AWS ECR o Google Artifact Registry. El plano de control tira desde el registry privado en lugar de Docker Hub.

Login de Docker Hub: si debes tirar desde Docker Hub, usa una cuenta autenticada para obtener el allowance correspondiente. Docker recomienda iniciar sesión en producción en vez de depender de pulls anónimos.

Caché de imágenes: el Docker daemon ya cachea layers descargados. Si los sandbox containers se borran y recrean con frecuencia, evita que los jobs de limpieza borren layers útiles demasiado rápido.

Aceleración de registry interno

Si el host está en una red privada, configura mirrors o proxies de registry para timeouts de Docker pull. Trátalo como parte de la plataforma, no como un parche tras la primera caída.

Checklist de troubleshooting: la preview URL no abre

Cuando una preview URL no abre, revisa estos cinco puntos en orden.

1. ¿DNS apunta a Traefik?

Revisa el wildcard DNS. El registro A o CNAME de *.preview.example.com debe apuntar a la IP del host donde corre Traefik.

Usa dig o nslookup:

dig sandbox123.preview.example.com

La IP devuelta debe ser la IP del host de Traefik, no otra dirección.

2. ¿Traefik descubrió los labels del container?

Revisa la configuración del Docker provider de Traefik y los labels del container.

Usa el dashboard de Traefik o los logs:

docker logs traefik-container | grep "sandbox123"

Los logs de Traefik deberían mostrar que descubrió la ruta de sandbox123. Si no, revisa:

  • Que exista el label traefik.enable=true
  • Que exposedByDefault: false esté bien configurado
  • Que Traefik tenga montado correctamente el Docker socket

3. ¿El container está corriendo?

Revisa el estado del sandbox container.

Usa docker ps:

docker ps | grep sandbox123

Si el container está detenido, puede que lo haya parado el idle reaper o que el wake path no lo haya reiniciado. Al visitar la preview URL, el wake handler del plano de control debería arrancar el container y devolver una warming page. Si falla, revisa los logs del plano de control.

4. Dirección donde escucha la aplicación

Revisa la dirección y el puerto donde escucha la aplicación dentro del container.

Entra al container y revisa puertos:

docker exec sandbox123 netstat -tuln

La aplicación debería escuchar en 0.0.0.0:3000, no solo en 127.0.0.1:3000. La documentación de Docker indica que los puertos vinculados a 127.0.0.1 o ::1 solo son accesibles desde el Docker host; las requests externas no llegan.

Si la aplicación solo escucha en localhost, cambia su configuración o usa el modo de red adecuado.

5. Revisión de port binding

Compara el puerto de servicio de Traefik con el puerto real de la aplicación.

El label de Traefik puede decir:

- "traefik.http.services.sandbox123.loadbalancer.server.port=3000"

La aplicación dentro del container debe escuchar en el puerto 3000. Si Traefik apunta a 3000 pero la app escucha en 8080, las requests fallan.

Cuando los puertos no coinciden, corrige los labels de Traefik o la configuración de la app.

Conclusión

Docker en un solo host más un plano de control en Go puede sostener un entorno de preview autohospedado: cada sandbox tiene preview URL independiente, los recursos están acotados y el tradeoff de seguridad queda claro. El encaje es estrecho: equipos internos, usuarios de confianza y concurrencia pequeña. Cuando la frontera de confianza se abre a desconocidos externos o la concurrencia supera un host, pasa a microVMs o Kubernetes.

Los módulos centrales son directos: la tabla de decisión mantiene honesto el alcance; el plano de control, Traefik, SQLite y los reapers explican las piezas móviles; las preview URLs dependen de Host rules de Traefik y Docker labels; la seguridad trata el Docker socket como poder de nivel host; los límites de recursos son la base multi-tenant; la caché de imágenes reduce riesgo de Docker Hub; y la checklist de troubleshooting da un camino cuando fallan las preview URLs.

Siguientes pasos:

  • Previews pequeñas para equipo interno: empieza con Docker single-host + plano de control en Go y mide densidad de recursos
  • Usuarios externos o workloads de alto riesgo: pasa a Docker Sandboxes, Firecracker u otra frontera microVM
  • Self-hosted CI Runner: lee la guía de GitHub Actions self-hosted runner para completar la infraestructura privada
  • Aplicaciones dentro del entorno de preview: usa la guía de Next.js Docker self-hosting para ejecutar la app dentro de la sandbox

Crear un MVP de Dev Sandbox autohospedado

Pasar de un host Docker único a un entorno interno de preview con límites de recursos y control de acceso.

⏱️ Estimated time: 4 hr

  1. 1

    Step 1: Elegir el modelo de aislamiento

    Usa Docker en un solo host para equipos de confianza y previews pequeñas. Para usuarios desconocidos o código no fiable, usa microVMs, hosts separados o Kubernetes.
  2. 2

    Step 2: Preparar el plano de control

    Ejecuta un servicio pequeño en Go que controle metadatos de sandbox, operaciones de ciclo de vida, reapers y wake-on-request.
  3. 3

    Step 3: Configurar la discovery de Traefik

    Activa el Docker provider con `exposedByDefault: false` y añade labels solo a los containers sandbox que deben recibir tráfico.
  4. 4

    Step 4: Asignar preview URLs estables

    Usa wildcard DNS como `*.preview.example.com` y enruta cada host `{sandbox_id}.preview.example.com` al puerto del container que sirve la aplicación.
  5. 5

    Step 5: Persistir los workspaces

    Guarda cada sandbox bajo `SANDBOXED_DATA_DIR/workspaces/` o un directorio equivalente del host para que `docker stop` no borre archivos del usuario.
  6. 6

    Step 6: Añadir límites de recursos

    Define límites de memoria, CPU y PIDs para cada sandbox. Un build descontrolado no debe dejar sin recursos al host.
  7. 7

    Step 7: Cerrar los puntos de entrada de producción

    No expongas la API de Docker. Añade autenticación de API, TLS, control de acceso para preview links, separación de red y audit logs.
  8. 8

    Step 8: Planificar imágenes y registry

    Precalienta imágenes comunes, inicia sesión en Docker Hub cuando haga falta y usa un registry privado o caché cuando crees sandboxes con frecuencia.

FAQ

¿En qué se diferencia un Dev Sandbox de Docker Compose?
Compose funciona mejor para un conjunto fijo de servicios de larga vida. Un plano de control de Dev Sandbox crea, arranca, detiene, despierta y destruye entornos bajo demanda, da a cada uno una preview URL estable y guarda estado para que un backend de producto pueda gestionarlos.
¿Por qué no usar Kubernetes desde el principio?
Kubernetes encaja mejor cuando necesitas scheduling multinodo, alta disponibilidad, network policies estándar y gobernanza de plataforma. Para validar el loop de producto con un equipo interno de confianza, un solo host Docker suele bastar.
¿El aislamiento de containers Docker sirve para ejecutar código arbitrario de desconocidos?
No conviene tratarlo como una frontera fuerte para ese caso. Un plano de control basado en Docker socket es adecuado para usuarios de confianza. El código arbitrario no fiable debería ir a microVMs, VMs dedicadas, gVisor, Kata, Firecracker o al menos hosts separados por tenant.
¿Una preview URL necesita HTTPS?
Las pruebas locales con `*.localhost` pueden empezar con HTTP. Los dominios públicos de preview deberían usar HTTPS, sobre todo si los usuarios introducen tokens, formularios o datos de negocio. Un certificado wildcard evita emitir un certificado por sandbox.
¿Se pierden los archivos después de un idle stop?
No si el workspace se guarda en un directorio persistente del host. `docker stop` libera recursos, pero conserva archivos. Mantén destroy y purge como operaciones separadas para dejar claro si se borra solo el container o también el workspace.
¿Los rate limits de Docker Hub pueden afectar esta arquitectura?
Sí. Crear sandboxes con frecuencia aumenta los image pulls. En producción conviene autenticarse en Docker Hub cuando haga falta, precalentar imágenes comunes y considerar un registry privado o caché de imágenes.

14 min de lectura · Publicado el: 5 jun 2026 · Actualizado el: 14 jul 2026

Ruta de lectura de la serieParte 1 de 1

Guía práctica de Docker

Estás leyendo el primer artículo de esta serie. Continúa con el siguiente o abre el hub para ver toda la ruta.

Ver hub de la serie

Anterior

Estás al inicio de esta serie.

Siguiente

Este es el artículo más reciente de la serie por ahora.

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog