Deploy com Docker Compose
Aprenda a fazer self-host do app kubojs com os Dockerfiles e docker-compose.yml gerados
Visão geral
Este guia explica como o kubojs empacota suas aplicações para self-hosting com Docker Compose. Você vai aprender:
- O que é gerado e como a stack do compose é ligada
- Como buildar, rodar e monitorar os containers
- Como as variáveis de ambiente fluem entre builds, containers e o arquivo compose
- Como funciona o serviço opcional de banco Docker
- O que mudar ao sair de containers locais para um host real
O que é gerado
Escolher docker como alvo de deploy gera:
docker-compose.ymlna raiz do repo define os serviçosweb,servere (opcionalmente) de banco, com health checks e ordem de startup.apps/*/Dockerfilesão builds multi-stage que instalam o workspace monorepo inteiro, buildam um app e embarcam uma imagem de runtime mínima.nginx.confé gerado para frontends SPA estáticos (TanStack Router, SolidJS), servidos por nginx em vez de um server Node.
Habilitando deploy Docker
Deploy combinado (web + server + banco):
npm create kubojs@latest my-app \
--frontend tanstack-router \
--backend hono \
--runtime bun \
--database postgres \
--db-setup docker \
--web-deploy docker \
--server-deploy dockerSó web (backend fullstack self):
npm create kubojs@latest my-app \
--frontend next \
--backend self \
--web-deploy dockerSó server:
npm create kubojs@latest my-app \
--frontend none \
--backend fastify \
--runtime node \
--server-deploy dockerO deploy de server exige runtime bun ou node. Adicionar --db-setup docker coloca um container Postgres, MySQL ou MongoDB no mesmo arquivo compose, com volume nomeado e health check.
Rodando a stack
bun docker:build # docker compose build
bun docker:up # docker compose up -d --build
bun docker:logs # docker compose logs -f
bun docker:down # docker compose downDepois de docker:up:
| Serviço | URL | Notas |
|---|---|---|
| web | http://localhost:3001 | nginx (frontends SPA) ou o server do framework |
| server | http://localhost:3000 | com health check; o web espera ele ficar healthy |
| database | localhost:5432 / 3306 / 27017 | somente com --db-setup docker |
A ordem de startup usa depends_on + condition: service_healthy: o banco precisa passar no health check antes do server iniciar, e o server precisa passar no health check fetch('http://localhost:3000/') antes do container web iniciar.
Com --db-setup docker você também ganha scripts de banco com escopo para desenvolvimento local sem a stack completa:
bun db:start # docker compose up -d postgres (just the database, detached)
bun db:watch # docker compose up postgres (with logs in the foreground)Como as imagens são buildadas
Cada Dockerfile é um build multi-stage com a raiz do monorepo como contexto de build:
- Um stage base instala o workspace com seu gerenciador de pacotes (com cache de dependências montado, para rebuilds rápidos).
- O app é buildado com
SKIP_ENV_VALIDATION=1, para o t3-env não exigir secrets reais no build da imagem. Projetos Prisma recebem umDATABASE_URLplaceholder paraprisma generate; o valor real chega em runtime pelo compose. - Um stage de runtime enxuto copia só a saída buildada. Frontends SPA vão para uma imagem nginx; frontends SSR e servers rodam em
node:24-slim(com o binário Bun adicionado quando necessário).
Variáveis de ambiente
Três regras cobrem tudo:
- Variáveis públicas do web são embutidas no build. Valores como
VITE_SERVER_URLsão build args do compose — ficam inline no bundle do client quando a imagem é buildada. O padrão gerado éhttp://localhost:3000(a porta publicada do server). Mudá-los exige rebuild da imagem, não só restart. - Variáveis de runtime vêm do arquivo
.envde cada app. O compose carregaapps/web/.enveapps/server/.envviaenv_file(marcados como opcionais, para arquivos ausentes não quebrarem o startup). - Overrides do compose cuidam do networking dos containers. Dentro da rede Docker, containers se alcançam pelo nome do serviço, não por
localhost— então o compose define valores comoDATABASE_URL: postgresql://postgres:...@postgres:5432/my-appeCORS_ORIGIN: http://localhost:3001no blocoenvironment:, que prevalece sobreenv_file. Seus.envlocais continuam apontando para localhost no desenvolvimento fora do Docker.
Senhas de banco têm padrão password e podem ser sobrescritas com variáveis do compose (POSTGRES_PASSWORD, MYSQL_PASSWORD, MONGO_PASSWORD) — defina-as em um .env na raiz ou exporte antes de docker:up.
Indo para um host real
O compose gerado já tem forma de produção (imagens multi-stage, health checks, restart: unless-stopped, volumes nomeados), mas algumas coisas ainda estão dimensionadas para localhost:
- Coloque um reverse proxy na frente. Termine TLS com Caddy, Traefik ou nginx e roteie seu domínio para o container web; pare de publicar as portas do server e do banco publicamente quando o proxy cuidar do roteamento.
- Atualize URLs embutidas para o seu domínio. Rebuild a imagem web com o build arg da URL pública do server apontando para a origem real da API, e defina
CORS_ORIGIN/BETTER_AUTH_URLno server com a origem real do web. - Defina secrets reais. Substitua a senha padrão do banco e coloque valores de produção no ambiente do server (
environment:do compose, um.envno host ou o secret store do seu orquestrador). - Faça backup do volume do banco. Os dados ficam em um volume Docker nomeado (
<project>_postgres_dataetc.);docker compose down -vapaga.
Troubleshooting
Porta já em uso
A stack publica 3001 (web), 3000 (server) e a porta do banco. Pare o que estiver usando essas portas ou edite os mapeamentos ports: em docker-compose.yml.
Mudei uma env pública mas o app web não pegou
Variáveis públicas são build args embutidos no bundle do client. Rebuild a imagem: bun docker:up (passa --build) ou bun docker:build antes.
O server não alcança o banco com URL localhost
Dentro do compose, o host do banco é o nome do serviço (postgres, mysql, mongodb), não localhost. Use o DATABASE_URL fornecido pelo compose (já definido no bloco environment:) em vez do de apps/server/.env.
O container web nunca inicia
Ele espera o health check do server. Veja bun docker:logs — se o server estiver em crash-loop (em geral env ausente ou DATABASE_URL ruim), o container web fica em estado waiting.
Erros de CORS no browser
O compose define CORS_ORIGIN: http://localhost:3001 quando o serviço web está presente. Se você acessar o app de outro host ou porta, atualize esse valor para a origem que está usando de fato e reinicie.