0011 — O container do app é artefato do repo, derivado de template¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-16 · Decidido em: 2026-07-16
Contexto¶
A plataforma nunca prescreveu Dockerfile. Não havia template, artefato rastreado,
item de checklist nem receita por stack — e a constituição não o mencionava em §
nenhum. Todo o conhecimento de container vivia no registro "não pise nesta mina":
armadilhas boas e verificadas (.dockerignore × docs/app.json, .git/HEAD no build,
ARG FLUTTER_VERSION × .fvmrc). O registro "isto é o que você constrói" nunca foi
escrito. Coolify dizia "Build Pack: Dockerfile"
tratando o arquivo como fato da natureza.
Havia contradição interna: a §5 exige health check obrigatório em todo app
deployável, mas a Estrutura padrão de cada repo (§3) não listava o artefato onde
o HEALTHCHECK vive. Prescrevia o parafuso, não a parede.
E era assimétrico por stack: flutter.md tinha a ponte jq→--dart-define;
java-micronaut.md não citava Dockerfile em linha nenhuma — a stack que mais
deploya no Coolify era a sem receita.
O caso concreto (loop §6, 2026-07-16): o thoms/int-produtos-ecom nasceu doc-only,
virou código, passou nos testes e no e2e local — e falhou no deploy do Coolify por não
ter Dockerfile. A cadeia: central não prescreve → o /x-planejar não tinha o que puxar
→ a T0.1 do plano pediu build.gradle + /health + app.json, sem container → a T7.1
escopou deploy como "operação humana (grant + env Coolify)" → o ci.yml roda só
./gradlew check, não builda imagem → o Coolify foi o primeiro a tocar o
Dockerfile, em produção. A recuperação foi copiar do irmão: o arquivo criado carrega
literalmente o comentário "Espelha o Dockerfile do integrador".
O integrador tem Dockerfile porque o plano dele tinha uma fase de deploy escrita à
mão, não porque o padrão mandou. Conhecimento vivo num repo, ausente no central, irmão
quebrado em produção — o mesmo formato do drift de cache de Flutter Web (backlog #84).
Decisão¶
App deployável leva Dockerfile e .dockerignore no repo desde o primeiro commit de
código, derivados de template rastreado, e o /xadm-release os exercita antes da tag.
- Piso na constituição §3 (Estrutura padrão): os dois arquivos entram na árvore, com
a razão — o Coolify builda o Dockerfile do repo, não há buildpack, e o
ci.ymlnão builda imagem. Fecha a contradição com o health check obrigatório da §5. - Templates rastreados e opcionais:
dockerfile-javaedockerignore-javaentram no manifesto (sha256, sincronizados pela/xadm-docs), opcionais como o kit de UI — biblioteca, CLI distribuído como artefato e app Flutter não os têm, e ausência aí não é defasagem. - Gate no pré-flight do
/xadm-release:docker build -t <slug>:pre .antes da tag. Sem daemon Docker → degradado (conferir que os arquivos existem) e declarado no relatório final, no mesmo padrão do gate de código degradado (#71) e da baseline inalcançável (#53) — meio gate é decisão explícita, não silêncio. - Detalhe operável na Engenharia: Java/Micronaut §Deploy (multi-stage, build-only, nome do jar fixo, Jammy × Alpine, flags de heap). Um dono por fato (§1.9); a constituição fica com o piso.
Alternativas consideradas¶
- Só receita em prosa na engenharia, sem template rastreado: cada app copiaria do doc
à mão. Preterido — é exatamente o xerox-do-irmão que gerou este feedback; sem
artefato rastreado a
/xadm-docsnão tem o que cobrar. docker buildcomo job doci.yml, em todo push: pega mais cedo que o release, mas custa minutos por push e exige dind no runner. Preterido por proporcionalidade — o pré-flight pega antes da tag, que é antes do deploy, que é onde dói.- Só item de conformidade no
/xadm-docs(checar existência): barato e pegaria este caso, mas não pega Dockerfile quebrado, só ausente. Preterido em favor do build real; o item de conformidade pode voltar como camada extra se o gate provar insuficiente. - Templatizar Flutter na mesma leva: preterido por ora — o Flutter já tem a ponte
jqdocumentada, e o backlog #84 decidiu explicitamente não templatizar omapde nginx até a receita rodar em mais de um app. Reabrir sem dado novo seria contradizer a decisão anterior. Só Java agora, que estava vazio.
Consequências¶
- App novo nasce deployável. O
/x-planejartem o que puxar para a fase de bootstrap; a falha do ecom não se repete por omissão. - O
Dockerfiledeixa de ser o único artefato do repo que nenhum CI toca. - Um bug latente em produção achado ao dogfoodar o template. Buildar o template contra
um app real (em vez de publicá-lo por inspeção) expôs que o
--mount=type=cache,target=/root/.gradleque os dois apps Java da casa já usam temiddefault = otargete sharing default =shared— ou seja, todos os apps Java dividem um cache mount no servidor do Coolify, e o Gradle não tolera acesso concorrente. Dois deploys ao mesmo tempo, ou um build morto que deixou lock stale, falham com "Timeout waiting to lock journal cache … It is currently in use by another process" — intermitente, com cara de bug do app. O template usaid=gradle-<slug>+sharing=locked(o que a doc do Docker prescreve para o apt, pela mesma razão). Migração oportunista nos apps existentes. - Semântica do
.dockerignoredocumentada e verificada, não inferida: o Docker casa padrões comfilepath.Matchdo Go (*não cruza/), não com formato.gitignore. Consequências que os repos atuais carregam sem saber:buildbare exclui só a raiz (sub/build/viaja pro daemon — latente até alguém modularizar), e**/*.jarquebraria o build ao excluir ogradle/wrapper/gradle-wrapper.jar. O template usa**/builde mantém*.jarraiz-only de propósito, com o porquê escrito. - Migração oportunista: os apps com Dockerfile à mão (
integrador,int-produtos-ecom) divergem do template no primeiro/xadm-docs. Reconciliar quando tocarem o container — o template é o destino, não um retrofit urgente. - Sem guarda offline (como 0009/0010): o
valida-frontmatternão valida Dockerfile. O enforcement é o gate do release + revisão de PR.