Como hospedar o sandboxd no seu próprio VPS
Veja como instalar o sandboxd com versão fixada, configurar chaves de modelo e HTTPS, além dos mínimos de RAM e disco e da limpeza de sandboxes antigas.
O que é o sandboxd e o que obtém ao executá-lo por conta própria
Para alojar o sandboxd por conta própria, precisa de um servidor Linux com Docker e de um nome de domínio. Envia um prompt, um agente de programação cria uma aplicação real dentro de um contentor isolado e essa aplicação fica disponível no seu próprio URL de pré-visualização. Os construtores de aplicações a partir de prompts são a categoria alojada mais visível de 2026, e o sandboxd é a opção que corre no seu VPS, sob a licença MIT, com o código gerado armazenado no seu próprio disco.
A arquitetura é pequena de propósito. Um plano de controlo Go controla o Docker, o Traefik v3 encaminha cada hostname de pré-visualização, o SQLite armazena o estado e cada aplicação corre dentro de um contentor. Não existe Kubernetes nem um servidor de base de dados separado. Por isso, uma máquina com 2 vCPU consegue executá-lo.
Quatro objetos representam todo o modelo. Uma app é o projeto persistente. Contém o nome, os metadados do git e os segredos. Uma sandbox é o contentor Docker onde essa app corre, e uma app aponta para uma sandbox de cada vez. Um workspace são os ficheiros da app. Estes ficam no host e persistem depois de o contentor terminar. Uma task é um prompt enviado ao agente dentro da sandbox. Parar uma sandbox liberta memória e preserva os ficheiros. Destruí-la elimina o contentor, e a app pode iniciar uma nova sandbox.
Como o sandboxd difere do Dify e do OpenHands?
Estes três projetos são frequentemente confundidos porque todos executam um LLM (modelo de linguagem de grande escala) no seu servidor, mas produzem resultados diferentes. O Dify cria aplicações com LLM: interfaces de chat, pipelines de recuperação e workflows que chamam um modelo sempre que alguém os utiliza. O modelo faz parte do produto final. O OpenHands trabalha num repositório que já existe: indique-lhe o seu código e ele lê ficheiros, executa comandos e propõe alterações. O sandboxd começa do zero. Cria a estrutura de um projeto a partir de um preset, compila-o num contentor novo e fornece um URL para o consultar. O resultado é uma aplicação React ou FastAPI comum que não precisa de um modelo para funcionar.
Por isso, escolha com base no resultado que pretende obter. O sandboxd serve para começar com uma frase e manter o código depois. Os outros dois servem para situações em que o repositório ou o produto baseado em modelos já existe.
A outra diferença é a idade. É o fator que deve ponderar antes de criar algo real com base nele.
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"
}
]O sandboxd tem 875 estrelas, contra 83,091 do OpenHands e 151,320 do Dify. O repositório foi criado em 3 de junho de 2026, por isso tinha dois meses em agosto de 2026. O OpenHands data de março de 2024 e o Dify de abril de 2023. A release v0.1.0 foi lançada em 6 de junho de 2026 e a v0.3.6 em 1 de agosto de 2026. O projeto identifica-se como beta e informa que as releases 0.x podem quebrar a compatibilidade. Interprete estes números como um indicador de risco das dependências, não como uma avaliação da qualidade: um projeto com dois meses de existência só teve dois meses para que outras pessoas encontrassem os seus bugs.
O que o servidor precisa e o que falha quando os recursos são insuficientes
O projeto indica que 2 vCPU e 4 GB de RAM são suficientes para começar. Isso é válido para o plano de controlo e uma sandbox pequena, mas não é suficiente para duas pessoas compilarem ao mesmo tempo. Distribua a memória por componentes. Traefik e o plano de controlo em Go consomem poucos recursos. Cada sandbox em execução contém uma toolchain completa de Node ou Python, e o pico ocorre durante um npm install seguido de uma compilação de produção. Planeie 8 GB para um servidor que manterá algumas aplicações ativas. Considere a swap uma rede de segurança, não capacidade adicional, porque uma compilação que usa swap demora minutos em vez de segundos.
Quando a memória se esgota, ocorrem duas falhas diferentes, que não se parecem. Dentro de uma sandbox, o contentor atinge o limite rígido de --memory definido pelo sandboxd, e o kernel termina o processo maior. A compilação falha sem uma mensagem útil do agente. docker ps -a mostra o código de saída 137 desse contentor, e docker inspect nesse contentor indica "OOMKilled": true. Uma compilação de Node que falha desta forma normalmente apresenta JavaScript heap out of memory primeiro.
A segunda falha ocorre no host. O sandboxd executa um reaper de pressão que para as sandboxes quando a memória do host fica baixa. Num servidor pequeno, uma sandbox pode desaparecer enquanto acompanha a pré-visualização. Os ficheiros permanecem seguros e o próximo pedido ao URL de pré-visualização reativa a sandbox, mas uma tarefa que estava em execução quando o contentor parou não é retomada.
O disco é o problema mais silencioso. Cada aplicação mantém o seu próprio workspace no host, e um projeto JavaScript inclui uma árvore node_modules com centenas de megabytes. Dez aplicações ocupam vários gigabytes com dependências, antes de contar as imagens. Comece com 40 GB e monitorize:
docker system df
sudo du -sh /var/lib/sandboxed/workspacesO diretório de dados predefinido é /var/lib/sandboxed, escrito com o e adicional. Escrever /var/lib/sandboxd devolve um diretório vazio e causa cinco minutos de confusão.
Instalar uma versão fixa do sandboxd
O Docker Engine com o plugin Compose, além do git, já deve estar instalado no servidor. Instalar o Docker numa VPS explica essa parte.
docker compose version
git --versionAmbos devem mostrar uma versão. docker: 'compose' is not a docker command significa que tem o binário autónomo antigo docker-compose, mas o instalador espera o plugin v2.
O instalador é um script shell obtido pela rede. Leia-o antes de o executar e fixe a versão.
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 é a referência do git que o instalador obtém em $HOME/.sandboxd/src, e o valor predefinido é main. Se não definir essa variável, a instalação corresponderá ao que tiver sido integrado nessa manhã. Isto é relevante num projeto que lançou seis versões apenas em julho de 2026. Fixe a versão e atualize-a deliberadamente depois de ler o changelog.
O script clona o código-fonte, cria as imagens, inicia a stack com docker compose up -d e mostra o URL da consola e um token de API no fim. Guarde esse token num local seguro. É a credencial de uma API que controla o Docker como root.
curl http://127.0.0.1:9090/healthzIsto mostra ok quando o plano de controlo está ativo. Se não mostrar nada, a stack não iniciou: execute docker compose ps a partir de ~/.sandboxd/src para ver qual serviço está parado e, em seguida, docker compose logs sandboxd para saber porquê.
Aceder à consola num servidor remoto
A consola é disponibilizada através do Traefik em HTTP_PORT, que por predefinição é 80, no hostname http://console.localhost. O Traefik encaminha as ligações com base no hostname. Por isso, introduzir o endereço IP do servidor no browser não corresponde a nenhuma regra e devolve 404. Até configurar um domínio real, encaminhe a porta e mantenha o hostname:
ssh -L 8080:127.0.0.1:80 you@your-vpsEm seguida, abra http://console.localhost:8080 no portátil. No Linux e no macOS, qualquer nome terminado em .localhost é resolvido para 127.0.0.1. Assim, o pedido segue pelo túnel com o cabeçalho Host correto. Defina a palavra-passe da consola no primeiro acesso.
Dê um modelo ao agente
A imagem base inclui dois agentes de codificação: OpenCode e Claude Code. SANDBOXD_DEFAULT_AGENT decide qual deles executa uma tarefa que não especifica um agente, e o padrão é opencode. Sem nenhuma chave configurada, as tarefas usam os modelos gratuitos sem chave do OpenCode Zen. Assim, a primeira build não tem custo e pode testar todo o ciclo antes de gastar.
Ligue a sua própria chave quando quiser um modelo mais potente. As chaves vão para o plano de controlo, nunca para a sandbox. São armazenadas de forma encriptada no diretório de dados e injetadas na rede por um proxy de credenciais. Assim, nem o agente nem o código que este escreve podem lê-las.
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-..."}'A consola faz o mesmo em Settings, AI Agents, incluindo um fluxo OAuth guiado se quiser usar uma subscrição Claude em vez de uma chave de API. O modelo predefinido de cada agente fica no mesmo painel, e uma tarefa individual pode substituí-lo.
Crie uma aplicação pequena de ponta a ponta
Crie a aplicação, inicialize o sandbox e envie um prompt. Os IDs são devolvidos como JSON, e o quickstart extrai-os com sed, portanto não é necessário ter 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"As duas variáveis devem conter um ID. Um $SB vazio significa que o sandbox nunca foi inicializado. A causa habitual é a imagem base ainda estar a ser criada ou o host estar sem memória. Um 401 no lugar de um ID significa que o bearer token está incorreto.
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"}'A resposta contém um ID de tarefa. GET /v1/sandboxes/$SB/tasks/<task id> devolve o resultado, e o caminho /events na mesma tarefa é um fluxo SSE (eventos enviados pelo servidor) em tempo real do que o agente está a fazer. A consola mostra o mesmo fluxo como uma conversa.
A aplicação fica disponível em http://s-<sandbox id>-3000.preview.localhost, onde 3000 é a porta solicitada. Se o sandbox estiver adormecido, o primeiro pedido chega ao catch-all do Traefik. O sandboxd inicia o contentor, aguarda até a porta responder e apresenta uma página curta de inicialização que é atualizada para a aplicação. Uma pré-visualização que nunca abandona essa página significa que o processo dentro do contentor não está a escutar na porta declarada no sandbox.yaml da aplicação.
Coloque os previews num domínio real com HTTPS
Cada sandbox recebe o seu próprio nome de host, pelo que um único registo DNS wildcard abrange todos. Aponte *.preview.yourdomain.com para o endereço IP do servidor com um registo A. Depois, defina as variáveis dos previews em .env, dentro de ~/.sandboxd/src:
PREVIEW_DOMAIN=yourdomain.com
PREVIEW_ENTRYPOINT=websecure
PREVIEW_TLS=true
SANDBOXD_API_AUTH_DISABLED=falseO Traefik precisa da configuração correspondente: ative o entrypoint websecure em traefik/traefik.yml e adicione um certificate resolver. Use o desafio DNS-01, porque um único certificado wildcard abrange todos os nomes de host dos previews. Com HTTP-01, cada sandbox novo precisaria da sua própria emissão, e uma tarde intensa de builds pode atingir diretamente os limites de rate da Let's Encrypt. Certificados wildcard através do desafio DNS-01 explica a configuração do DNS.
cd ~/.sandboxd/src
docker compose up -dOs URLs dos previews passam a ser https://s-<id>-3000.preview.yourdomain.com. Abra as portas 80 e 443 na firewall e mantenha a porta 9090 fechada para a Internet: consulte regras básicas da firewall ufw. Lembre-se de que qualquer pessoa que consiga adivinhar o nome de host de um preview pode carregar a aplicação, pelo que deve tratar os previews como públicos.
Onde é guardado o código gerado e é possível exportá-lo?
No host, dentro do diretório de dados. Cada workspace é um diretório normal em /var/lib/sandboxed/workspaces/<id>/, montado através de bind mount no container, e os ficheiros da aplicação ficam em /home/sandbox/workspace/app dentro do sandbox. O estado do control plane está num único ficheiro SQLite em state/sandboxd.db, e as credenciais encriptadas do agente estão em agent-auth/. Nada fica oculto numa camada do container. Por isso, uma cópia de segurança consiste em copiar o diretório e esse ficheiro de base de dados. cópias de segurança com restic num VPS trata de ambos.
sudo ls /var/lib/sandboxed/workspaces
sudo du -sh /var/lib/sandboxed/workspaces/*A exportação para Git está integrada, em vez de ser adicionada posteriormente. A API disponibiliza o estado e o diff para leitura, seguidos de commit e 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"}'Um remote privado precisa de um personal access token, definido uma vez na consola, em Settings, Git credentials. O token é armazenado de forma encriptada e permanece fora do sandbox. Assim, o agente não o consegue ler nem utilizá-lo para fazer push sem o seu conhecimento. Faça push cedo e com frequência. Até o fazer, o diretório do workspace é a única cópia do código, e DELETE /v1/apps/<id> remove-o sem possibilidade de recuperação.
Qual é o custo de uma compilação em tokens do modelo?
O sandboxd não mede os seus gastos. Por isso, o número relevante está no console do seu provedor. Os modelos gratuitos do OpenCode Zen não têm custo. São mais lentos e menos eficientes do que um modelo pago. Isso aparece como mais rodadas de correção em qualquer aplicação que ultrapasse um protótipo simples.
O formato da cobrança acompanha o funcionamento de um loop de agente. A cada turno, o contexto necessário é enviado novamente. Por isso, o custo acompanha o número de turnos, não o número de aplicações. Um prompt que funciona à primeira é barato. Quinze rodadas de "agora corrija o espaçamento" num projeto com cinquenta ficheiros não são baratas, porque o conteúdo dos ficheiros é enviado novamente a cada turno. Os tokens de entrada e de saída têm preços diferentes, e o custo de um agente de programação por sessão apresenta um intervalo realista. Defina um limite máximo de gastos no provedor antes de entregar as credenciais a um loop executado sem supervisão.
Limpando sandboxes obsoletos
O mecanismo de remoção de inativos interrompe qualquer sandbox que esteja inativo por mais de SANDBOXD_IDLE_THRESHOLD_SECONDS, cujo valor padrão é 2100 segundos, ou 35 minutos. Isso libera a RAM e mantém os ficheiros. O próximo pedido à URL de pré-visualização reativa o contentor. Reduza esse valor num servidor pequeno, porque 35 minutos de contentores inativos representam 35 minutos de memória que não pode utilizar.
Interromper não é eliminar, e é assim que os discos ficam cheios sem chamar atenção. Um sandbox interrompido ainda mantém o seu workspace e o seu contentor. Remover o sandbox, mas manter a aplicação, é um DELETE no sandbox. Essa operação remove o contentor e o workspace. Remover a aplicação elimina tudo permanentemente.
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 itDepois de algumas semanas de experiências, docker system df mostrará mais espaço de imagens recuperável do que espera, porque cada aplicação que obteve o seu próprio toolchain deixou camadas para trás. docker image prune limpa as camadas sem referência. Verifique GET /v1/apps primeiro, porque uma imagem ainda referenciada por um sandbox em espera não é lixo.
O que o limite do contentor oferece e o que não oferece
Cada sandbox é executada com um utilizador sem privilégios, um sistema de ficheiros raiz apenas para leitura, todas as capacidades Linux removidas, no-new-privileges definido, um limite de memória e um limite de processos. O projeto é claro quanto a esta limitação: um contentor Linux com kernel partilhado é um limite de isolamento forte e um limite de segurança fraco. Um erro no kernel compromete o host.
Dois factos exigem medidas. A saída de rede de uma sandbox está aberta na compilação self-hosted, pelo que o código gerado pode aceder à internet, à sua rede local e aos endpoints de metadados da cloud. Existe um subsistema de saída nftables no código-fonte, mas ele é desativado na compilação portátil com Docker Compose. Por isso, os limites têm de ser aplicados pela firewall do host. Além disso, a API do plano de controlo tem efetivamente privilégios de root no host, porque controla o socket do Docker. Por predefinição, ela fica vinculada a 127.0.0.1:9090, SANDBOXD_API_AUTH_DISABLED deve permanecer false e a API nunca deve ser publicada na internet.
Se pretende permitir que outras pessoas enviem prompts para a sua máquina, esse modelo é demasiado fraco por si só. O projeto recomenda gVisor com SANDBOXD_RUNTIME=runsc, que coloca um kernel em espaço de utilizador entre a sandbox e o host e torna o trabalho intensivo em chamadas de sistema aproximadamente 1.7 a 4 vezes mais lento. A opção mais forte é usar uma máquina por tenant, pelo mesmo motivo que executar agentes de programação numa VM descartável.
É aconselhável criar algo com base num projeto com dois meses?
Para uma máquina de desenvolvimento pessoal, sim, com as precauções habituais: fixe SANDBOXD_REF, faça cópias de segurança de /var/lib/sandboxed e envie cada aplicação importante para um repositório git remoto. Para qualquer aplicação utilizada por clientes, aguarde pela versão 1.0 ou reserve capacidade para lidar com falhas, porque os responsáveis dizem claramente que a versão 0.x pode sofrer alterações incompatíveis. Os responsáveis também vendem uma instalação gerida por 79 dólares por mês, em agosto de 2026. Este dado é útil para avaliar se existe uma razão para o projeto continuar ativo.
O risco é aceitável por causa do resultado. sandboxd produz uma aplicação normal num repositório git normal. Se o desenvolvimento do projeto parar, mantém o código e perde apenas o wrapper. Esta é uma posição muito melhor do que a de um builder alojado que controla o seu projeto. Para uma visão mais ampla do que merece um lugar no seu servidor este ano, consulte o que vale a pena alojar por conta própria em 2026.
FAQ
Quais são os requisitos mínimos do servidor para o sandboxd?
O projeto indica que 2 vCPU e 4 GB de RAM são suficientes para começar. Isto cobre o plano de controlo, o Traefik e um sandbox pequeno. Use 8 GB e 40 GB de disco se quiser manter várias aplicações ativas ao mesmo tempo, porque cada sandbox em execução contém uma toolchain completa de Node ou Python e cada workspace mantém a sua própria árvore de dependências no disco. Quando o host fica sem memória, o pressure reaper do sandboxd para sandboxes para libertar memória. Se uma compilação exceder o limite de memória do contentor, o kernel termina o processo: docker ps -a mostra o código de saída 137.
Em que difere o sandboxd do Dify ou do OpenHands?
Eles produzem artefactos diferentes. O Dify cria aplicações que chamam um modelo em runtime, como interfaces de chat e pipelines de retrieval. O OpenHands edita um repositório que já existe, executa comandos e propõe alterações ao código existente. O sandboxd cria um projeto novo a partir de um prompt, compila-o dentro do seu próprio contentor e disponibiliza-o num URL de pré-visualização. O resultado é uma aplicação web normal que não precisa de um modelo para funcionar.
Onde fica efetivamente o código escrito pelo agente?
Fica no sistema de ficheiros do host, não dentro de uma imagem de contentor. Cada aplicação recebe um diretório em /var/lib/sandboxed/workspaces/<id>/, que é montado como bind mount no respetivo sandbox. Os ficheiros ficam disponíveis em /home/sandbox/workspace/app dentro do sandbox. O estado do plano de controlo está num único ficheiro SQLite em state/, no mesmo diretório de dados. Pode fazer commit e push para um remote Git a partir do separador Git da consola ou através dos endpoints /v1/apps/<id>/git/commit e /git/push. Um token para remotes privados é armazenado de forma encriptada pelo plano de controlo, em vez de ser entregue ao sandbox.
É seguro expor o sandboxd à Internet?
Exponha os URLs de pré-visualização e a consola, mas nunca a API do plano de controlo. Essa API controla o Docker no host, pelo que equivale a root. Por esse motivo, fica associada a 127.0.0.1:9090 por predefinição. Os sandboxes também têm saída de rede aberta na versão self-hosted. Isto significa que o código escrito pelo agente pode aceder à sua rede local e a endpoints de metadados da cloud. Adicione regras de firewall no host se o servidor tiver sistemas vizinhos que precisem de proteção. Para prompts enviados por pessoas em quem não confia, use um host por tenant em vez de depender do isolamento do contentor.