Alternar tema

Dev Sandbox self-hosted com Docker e Go: preview URLs sem Kubernetes

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

"O README do sandboxed descreve plano de controle em Go, Docker, Traefik, SQLite, preview URLs, idle stop e limites de hardening em produção."

"A documentação do Docker sobre resource constraints explica que containers não têm limites de CPU ou memória sem configuração explícita."

"A documentação do Docker Sandboxes usa microVMs, Docker daemon independente, isolamento de rede e isolamento de credenciais como modelo de segurança mais forte."

"O Docker provider do Traefik pode descobrir configuração de roteamento a partir de Docker labels e rotear com Host rules para serviços em containers."

Abrir um ambiente de preview para cada PR normalmente leva a Vercel ou Netlify. Mas, se custo, dados em rede privada ou controle de infraestrutura pesam mais, um único host Docker com um plano de controle em Go pode cobrir a mesma necessidade inicial. Cada sandbox tem sua própria preview URL, os recursos têm limites e a fronteira de segurança fica explícita. Sem Kubernetes e sem múltiplos nós.

Tabela de decisão: quando usar cada opção

Quando surge a demanda por “ambientes de preview self-hosted”, muita gente pensa primeiro em scripts com vários docker run, ou pula direto para Kubernetes. Comece pela tabela.

CenárioSolução recomendadaMotivo
Equipe interna com menos de 10 previews concorrentes e fronteira de confiança dentro da equipeDocker em um único host + plano de controle em GoA densidade de recursos é controlável, a arquitetura fica simples e você não opera um cluster K8s
Equipe interna com mais de 20 previews concorrentes ou necessidade de alta disponibilidade multinóK8s + isolamento por NamespaceUm host não aguenta; você precisa de scheduling entre nós e rolling upgrade
Usuários externos ou execução de código não confiável, incluindo agentes autônomosmicroVMs, como Docker Sandboxes ou FirecrackerDocker socket equivale a muito poder sobre o host e não deve rodar junto de workloads não confiáveis
Preview estática simples sem persistênciaShell script + porta aleatóriaFunciona, mas a preview URL não é estável e não há limites sólidos de recursos ou segurança

Use três critérios.

Tamanho da equipe: um único host combina melhor com até cerca de 10 previews internas concorrentes. Uma conta prática: 512 MB de RAM + 0,5 CPU por sandbox. Um host com 16 GB aguenta perto de 20 sandboxes antes da margem ficar apertada. Acima disso, use scheduling com K8s ou divida capacidade com microVMs.

Fronteira de confiança: membros internos e usuários confiáveis podem rodar em Docker containers. Se desconhecidos externos ou agentes autônomos executam código arbitrário, o design com Docker socket não é seguro o bastante. Docker Sandboxes usa isolamento por microVM; cada sandbox tem seu próprio Docker daemon, filesystem e rede.

Caminho de upgrade: comece com Docker em um único host para validar o loop de produto e a densidade de recursos. Se a concorrência passar do host ou a alta disponibilidade multinó virar requisito, vá para Kubernetes. Se a fronteira de confiança mudar de “equipe interna” para “usuários externos”, vá para microVMs.

O README de tastyeffectco/sandboxes deixa claro o alvo: AI app builders, agent platforms e coding playgrounds em Docker single-host. Não é isolamento de nível microVM; é um plano de controle em Go que cria containers em um único Docker host e expõe preview URLs.

Arquitetura por dentro: componentes centrais do plano de controle

Um ambiente de preview Docker single-host não é só um docker run em loop. Ele precisa de um plano de controle para ciclo de vida, registro de rotas e limpeza de recursos. A arquitetura de tastyeffectco/sandboxes se divide em seis módulos.

Plano de controle em Go (sandboxd): roda em um container e monta o Docker socket do host mais um diretório de dados. Gerencia o ciclo de vida de sandbox containers via Docker CLI: criar, iniciar, parar e remover. Todos os metadados de sandbox ficam no SQLite como source of truth.

Montagem do Docker socket: é a entrada do plano de controle no Docker daemon. Ao montar /var/run/docker.sock, sandboxd ganha permissão para criar e gerenciar containers. Essa é a fronteira de privilégio principal: com o Docker socket, o plano de controle tem permissões muito altas no host.

Registro com Traefik labels: quando um sandbox container inicia, o plano de controle injeta configuração de roteamento do Traefik por Docker labels. Traefik atua como reverse proxy, descobre rotas pelos labels e envia tráfego de *.preview.example.com ao container correto.

Armazenamento de metadados em SQLite: cada sandbox tem um ID único e um diretório correspondente. Metadados ficam no SQLite. Workspaces vivem em SANDBOXED_DATA_DIR/workspaces/, um subdiretório por sandbox para código-fonte, configuração e artefatos.

Idle reaper e pressure reaper: o idle reaper verifica por quanto tempo o sandbox container ficou ocioso e o para ao passar do limite, liberando RAM. O pressure reaper monitora pressão de memória do host e para algumas sandboxes antes de um OOM no host. Esses reapers são o núcleo da recuperação de recursos.

Wake path: depois que o idle reaper para um sandbox container, o primeiro acesso à preview URL precisa acordá-lo. Um catch-all do Traefik encaminha a request ao plano de controle; o plano inicia o container e devolve uma warming page até o app ficar pronto.

A versão mínima útil é: container do plano de controle em Go, montagem do Docker socket, Traefik, SQLite e idle reaper. O quick start local precisa do Docker Engine e do plugin Compose.

Implementação da preview URL

O ponto importante de uma preview URL é um domínio estável, não uma porta aleatória. Cada sandbox recebe um host independente {sandbox_id}.preview.example.com.

Configuração do Docker provider do Traefik

Traefik pode descobrir configuração de roteamento a partir de labels de containers pelo Docker provider. Configuração mínima:

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

exposedByDefault: false significa que Traefik só descobre containers que entram explicitamente por labels.

Host rule e Docker labels

O plano de controle injeta labels quando cria um sandbox container. Exemplo:

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"

A Host rule envia requests de sandbox123.preview.example.com para esse container. loadbalancer.server.port informa a porta interna em que o app escuta.

Caminho wake-on-request

Depois que o idle reaper para o sandbox container, o primeiro acesso à preview URL dispara o wake flow:

  1. DNS resolve para Traefik; isso exige wildcard DNS como *.preview.example.com
  2. Traefik conhece a rota da sandbox, mas o container está parado
  3. O catch-all do Traefik encaminha a request ao wake handler do plano de controle
  4. O plano de controle inicia o sandbox container e devolve uma warming page
  5. Quando o container fica pronto, Traefik encaminha as requests seguintes direto ao container

O catch-all precisa ter prioridade menor que todos os 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"

Requests que não batem em uma rota viva de sandbox caem no catch-all, e o plano de controle executa a lógica de wake.

Fronteira de segurança: permissões do Docker socket

Montar o Docker socket dá ao plano de controle poder de nível host. Esse é o tradeoff de segurança desta arquitetura: serve para equipes internas e usuários confiáveis, não para execução de código não confiável.

Riscos do Docker socket

A documentação oficial do Docker avisa que o daemon tem superfície de ataque. Se a API Docker for exposta de forma insegura, um usuário remoto sem root pode obter acesso root ao host. Um container com /var/run/docker.sock montado pode usar Docker CLI para criar, modificar e remover containers. Também pode alcançar filesystem e rede do host por containers que ele cria.

Isso significa:

  • O container do plano de controle tem permissões muito altas no host
  • Não misture plano de controle e workloads não confiáveis no mesmo host
  • Código dentro de sandbox containers pode afetar indiretamente o host se conseguir influenciar o plano de controle

Fronteira para cenários não confiáveis

Docker single-host + plano de controle em Go serve para:

  • Ambientes de preview para membros de equipe interna
  • Coding playgrounds para usuários confiáveis
  • Ambientes internos de validação para AI app builders ou agent platforms

Não serve para:

  • Execução de código arbitrário de usuários externos desconhecidos
  • Execução em produção de autonomous agents quando o host não é confiável
  • Plataformas multi-tenant que exigem isolamento forte

Se a fronteira de confiança muda de “equipe interna” para “usuários externos”, vá para Docker Sandboxes oficiais ou microVMs estilo Firecracker. Docker Sandboxes inclui isolamento por hypervisor, rede independente, Docker daemon independente, filesystem independente e isolamento de credenciais. Cada sandbox é uma microVM completa, não um container compartilhando o Docker daemon do host.

Checklist de hardening em produção

Antes de produção, adicione estas fronteiras:

  • Isolamento de rede: plano de controle e sandbox containers rodam em redes dedicadas, não na rede de negócio
  • Autenticação de API: quick starts locais podem vir sem auth; produção precisa de token ou outro mecanismo de autenticação
  • Exposição mínima: exponha preview URLs via Traefik, não a porta da API Docker
  • TLS: preview URLs devem usar certificado TLS wildcard, não HTTP em claro
  • Logs e monitoring: registre requests da API do plano de controle e eventos de ciclo de vida de containers, depois gere alertas

Para fronteiras mais fortes, compare gVisor, Firecracker e Kubernetes em um design de sandbox para AI agents.

Limites de recursos: memory / CPU / PIDs

Docker containers não têm limites de recursos por padrão. Sem limites, um sandbox container pode consumir RAM e CPU do host e afetar outras sandboxes e processos do host. Um ambiente de preview multi-tenant precisa de limites duros por container.

Comportamento padrão do Docker

A documentação do Docker explica que containers podem usar recursos do host conforme permitido pelo scheduler do kernel. Sem --memory ou --cpus explícitos, um container pode tomar recursos disponíveis do host.

Limite duro de memória

--memory define a memória máxima disponível para o container. Exemplo:

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

--memory-swap define o limite combinado de memória + swap. Se --memory-swap for igual a --memory, o container não usa swap.

Se o container ultrapassar o limite de memória, o OOM killer pode encerrar processos do container. Um OOM no host também pode afetar outros containers e processos do host.

Limite de cota de CPU

--cpus define quanta capacidade de CPU um container pode usar. Exemplo:

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

O container pode usar no máximo 0,5 CPU. Com muitas sandboxes concorrentes, o limite de CPU impede um container de consumir toda a capacidade de computação.

Limite de número de processos

--pids-limit ajuda a evitar fork bombs. Exemplo:

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

O container pode criar no máximo 100 processos. Depois disso, fork() falha.

Exemplo de configuração com Compose

Com Docker Compose, configure limites em deploy.resources.limits:

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

deploy.resources no Compose vale no modo Docker Swarm. Em Docker de um único host, passe --memory, --cpus e --pids-limit manualmente, ou rode docker-compose --compatibility.

O plano de controle deve injetar esses parâmetros ao criar sandbox containers, em vez de depender de configuração manual do usuário.

Operação: cache de imagens e rate limits do Docker Hub

Quando muitas sandboxes são criadas e destruídas com frequência, image pulls viram gargalo. Docker Hub tem pull rate limits e abuse rate limits, com políticas que mudam por tipo de conta e plano. Produção não deve depender de baixar cada imagem de sandbox do Docker Hub público toda vez.

Limites do Docker Hub

A documentação do Docker explica que usuários anônimos, usuários autenticados e contas de equipe têm políticas diferentes de pull rate. Ao ultrapassar o limite, requests de pull são rejeitadas. Números concretos mudam com a política; consulte Docker Hub usage and limits em vez de fixar valores.

Em cenários multi-sandbox, isso pesa porque:

  • Criar sandbox com frequência pode baixar uma imagem a cada início
  • Reiniciar depois de idle cleanup pode precisar da imagem de novo
  • Várias sandboxes podem baixar repetidamente a mesma imagem

Pré-aquecimento de imagens e estratégia de cache

Produção precisa de algumas medidas.

Pré-aquecimento de imagens: baixe imagens comuns no host antes de iniciar o plano de controle. Isso reduz a espera quando uma sandbox inicia.

Registry privado: envie imagens comuns para um registry privado, como Harbor, AWS ECR ou Google Artifact Registry. O plano de controle baixa do registry privado em vez do Docker Hub.

Login no Docker Hub: se precisar baixar do Docker Hub, use uma conta autenticada para obter a cota adequada. Docker recomenda login em produção em vez de depender de pulls anônimos.

Cache de imagens: o Docker daemon já cacheia layers baixadas. Se sandbox containers são removidos e recriados com frequência, evite que jobs de limpeza apaguem layers úteis agressivamente.

Aceleração de registry interno

Se o host está em uma rede privada, configure mirrors ou proxies de registry para timeouts de Docker pull. Trate isso como parte da plataforma, não como correção depois da primeira queda.

Checklist de troubleshooting: preview URL não abre

Quando uma preview URL não abre, verifique estes cinco pontos em ordem.

1. DNS aponta para Traefik?

Verifique o wildcard DNS. O registro A ou CNAME de *.preview.example.com deve apontar para o IP do host onde Traefik roda.

Use dig ou nslookup:

dig sandbox123.preview.example.com

O IP retornado deve ser o IP do host Traefik, não outro endereço.

2. Traefik descobriu os labels do container?

Verifique a configuração do Docker provider do Traefik e os labels do container.

Use o dashboard do Traefik ou os logs:

docker logs traefik-container | grep "sandbox123"

Os logs do Traefik devem mostrar a rota de sandbox123. Se não mostrarem, verifique:

  • Se o label traefik.enable=true existe
  • Se exposedByDefault: false está configurado corretamente
  • Se Traefik montou corretamente o Docker socket

3. O container está rodando?

Verifique o estado do sandbox container.

Use docker ps:

docker ps | grep sandbox123

Se o container estiver parado, talvez o idle reaper o tenha parado, ou o wake path não conseguiu reiniciá-lo. Ao visitar a preview URL, o wake handler do plano de controle deve iniciar o container e devolver uma warming page. Se isso falhar, verifique os logs do plano de controle.

4. Endereço em que o app escuta

Verifique o endereço e a porta em que o app escuta dentro do container.

Entre no container e inspecione as portas:

docker exec sandbox123 netstat -tuln

O app deve escutar em 0.0.0.0:3000, não só em 127.0.0.1:3000. A documentação do Docker informa que portas vinculadas a 127.0.0.1 ou ::1 só são acessíveis pelo Docker host; requests externas não chegam.

Se o app escuta só em localhost, ajuste a configuração do app ou use o modo de rede adequado.

5. Checagem de port binding

Compare a porta configurada no Traefik com a porta real do app.

O label do Traefik pode ser:

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

O app dentro do container deve escutar na porta 3000. Se Traefik aponta para 3000, mas o app escuta em 8080, as requests falham.

Quando as portas não batem, corrija os labels do Traefik ou a configuração do app.

Conclusão

Docker em um único host com plano de controle em Go consegue sustentar um ambiente de preview self-hosted: cada sandbox tem sua preview URL, os recursos são limitados e o tradeoff de segurança é explícito. O encaixe é estreito: equipes internas, usuários confiáveis e baixa concorrência. Se a fronteira de confiança se abrir para desconhecidos externos ou a concorrência passar de um host, vá para microVMs ou Kubernetes.

Os módulos centrais são diretos: a tabela de decisão mantém o escopo honesto; plano de controle, Traefik, SQLite e reapers explicam as peças móveis; preview URLs dependem de Host rules do Traefik e Docker labels; a segurança trata Docker socket como poder de nível host; limites de recursos são a base multi-tenant; cache de imagens reduz risco do Docker Hub; e a checklist de troubleshooting dá um caminho quando preview URLs quebram.

Próximos passos:

  • Previews pequenas para equipe interna: comece com Docker single-host + plano de controle em Go e meça densidade de recursos
  • Usuários externos ou workloads de alto risco: vá para Docker Sandboxes, Firecracker ou outra fronteira microVM
  • Self-hosted CI Runner: leia o guia de GitHub Actions self-hosted runner para completar a infraestrutura privada
  • Apps dentro do ambiente de preview: use o guia de Next.js Docker self-hosting para rodar o app dentro da sandbox

Criar um MVP de Dev Sandbox self-hosted

Do host Docker único a um ambiente interno de preview com limites de recursos e controle de acesso.

⏱️ Estimated time: 4 hr

  1. 1

    Step 1: Escolher o modelo de isolamento

    Use Docker em um único host para equipes confiáveis e workloads pequenos de preview. Para usuários desconhecidos ou código não confiável, use microVMs, hosts separados ou Kubernetes.
  2. 2

    Step 2: Preparar o plano de controle

    Execute um pequeno serviço em Go que controla metadados de sandbox, operações de ciclo de vida, reapers e wake-on-request.
  3. 3

    Step 3: Configurar a discovery do Traefik

    Ative o Docker provider com `exposedByDefault: false` e adicione labels apenas aos containers sandbox que devem receber tráfego.
  4. 4

    Step 4: Atribuir preview URLs estáveis

    Use wildcard DNS como `*.preview.example.com` e roteie cada host `{sandbox_id}.preview.example.com` para a porta do container que serve o app.
  5. 5

    Step 5: Persistir os workspaces

    Salve cada sandbox em `SANDBOXED_DATA_DIR/workspaces/` ou em um diretório equivalente do host, para que `docker stop` não apague arquivos do usuário.
  6. 6

    Step 6: Adicionar limites de recursos

    Defina limites de memória, CPU e PIDs para cada sandbox. Um build descontrolado não deve esgotar o host.
  7. 7

    Step 7: Fechar as entradas de produção

    Não exponha a API do Docker. Adicione autenticação de API, TLS, controle de acesso para preview links, separação de rede e audit logs.
  8. 8

    Step 8: Planejar imagens e registry

    Pré-aqueça imagens comuns, faça login no Docker Hub quando necessário e use um registry privado ou cache quando criar sandboxes com frequência.

FAQ

Qual é a diferença entre um Dev Sandbox e Docker Compose?
Compose é melhor para um conjunto fixo de serviços de longa duração. Um plano de controle de Dev Sandbox cria, inicia, para, desperta e destrói ambientes sob demanda, dá a cada um uma preview URL estável e registra estado para que um backend de produto possa gerenciá-los.
Por que não usar Kubernetes desde o começo?
Kubernetes se encaixa melhor quando você precisa de scheduling multinó, alta disponibilidade, network policies padronizadas e governança de plataforma. Para validar o loop de produto com uma equipe interna confiável, um único host Docker costuma bastar.
O isolamento de containers Docker serve para executar código arbitrário de desconhecidos?
Não trate isso como uma fronteira forte para esse caso. Um plano de controle baseado em Docker socket é adequado para usuários confiáveis. Código arbitrário não confiável deve ir para microVMs, VMs dedicadas, gVisor, Kata, Firecracker ou pelo menos hosts separados por tenant.
Uma preview URL precisa de HTTPS?
Testes locais com `*.localhost` podem começar com HTTP. Domínios públicos de preview devem usar HTTPS, especialmente quando usuários digitam tokens, formulários ou dados de negócio. Um certificado wildcard evita emitir um certificado por sandbox.
Os arquivos somem depois de um idle stop?
Não se o workspace estiver salvo em um diretório persistente do host. `docker stop` libera recursos, mas mantém arquivos. Separe destroy e purge para deixar claro se você remove só o container ou também o workspace.
Rate limits do Docker Hub podem afetar essa arquitetura?
Sim. Criar sandboxes com frequência aumenta image pulls. Em produção, autentique-se no Docker Hub quando necessário, pré-aqueça imagens comuns e considere um registry privado ou cache de imagens.

13 min de leitura · Publicado em: 5 jun 2026 · Atualizado em: 14 jul 2026

Trilha de leitura da sérieParte 1 de 1

Guia prático Docker

Você está lendo o primeiro post desta série. Continue para o próximo ou abra o hub da série para ver toda a trilha.

Ver hub da série

Anterior

Você está no início desta série.

Próximo

Este é o post mais recente da série até agora.

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog