Operação no Coolify¶
Como criar, configurar e operar uma aplicação no Coolify da X-Adm. Os exemplos usam nomes do BI Vantroba; troque pelos do seu app.
Como a rede funciona¶
O Traefik (proxy do Coolify) termina o TLS na borda e encaminha para a porta
interna do container. O PostgreSQL roda em outro projeto do Coolify
(000-compartilhado, recurso postgresql18) — o hostname JDBC é o nome do
serviço Docker na rede interna, nunca localhost nem IP externo.
flowchart TD
I[Internet] -- "HTTPS 443" --> T[Traefik]
T -- "HTTP porta interna" --> A[Container do app<br/>projeto: vantroba-bi]
A -- "JDBC 5432, rede interna" --> P[(postgresql18<br/>projeto: 000-compartilhado)]
Criar uma aplicação nova¶
Projects→ selecionar/criar o projeto →+ New Resource.- Deploy from Git Repository (o Coolify builda o Dockerfile do repo).
Build Pack:
Dockerfile. O repo tem que trazer oDockerfile+.dockerignore(constituição §3) — não há buildpack que adivinhe a stack, e oci.ymlnão builda imagem: aqui é o primeiro lugar onde eles rodam. Derive-os dos templates. Domains→ adicionarhttps://<subdominio>.xadm.biz(DNS A apontando para o servidor; o certificado Let's Encrypt é automático).Environment Variables→ configurar; marcar senhas e tokens como Secret (ficam ocultos nos logs e na UI).Storages→ mapear volumes para o que precisa sobreviver a redeploy (ex.:/app/data, uploads). Log de servidor não entra aqui: por engenharia/java o servidor loga em stdout e o Coolify captura — volume em/app/logsé o arquétipo errado. O diretório tem que existir na imagem com donoapp(mkdir -p+chownantes doUSER app, ver o Dockerfile): volume nomeado herda o dono do diretório da imagem, e se o diretório não existir ele nasceroot— o app não escreve. Bind mount não herda dono nenhum: aí o dono no host é que precisa ser1001:1001.- Health check: path
/health, porta interna do app, intervalo 30s. Deploye aguardar build + health check verde.
Build Arguments × Environment Variables (pega-pé recorrente)¶
O Coolify tem dois lugares para valores, e eles entram em momentos diferentes do ciclo — trocar os lados é falha silenciosa.
- Build Argument (+ marcar "Available at Buildtime"): chega ao
docker buildcomoARG. Use para tudo que o build consome e embute na imagem —ARG FLUTTER_VERSION,SOURCE_COMMITe todo valor que o Flutter embarca via--dart-define(ex.SENTRY_DSN, endpoint de release). Um--dart-definelê a variável na hora do build; se ela só existe como env de runtime, chega vazia no bundle e o app sobe sem ela, sem erro. - Environment Variable (runtime): chega ao processo em execução, não ao build. Use para o que o app lê no startup — URL do JDBC, secrets lidos em runtime, flags de execução.
Regra de bolso: o build precisa? → Build Argument. Só o processo lê? →
Environment Variable. Erra nos dois sentidos: valor de build configurado só
como runtime não chega ao build; e segredo de runtime marcado como build
arg vaza como ARG na imagem (foi o caso dos DOCS_S3_*). Não marque
"Available at Buildtime" no que não for de build.
Carimbar o commit do deploy: NÃO leia o .git/HEAD no build¶
Tentação que não funciona: ler .git/HEAD no Dockerfile para carimbar o
commit (ex. /commit.txt). O Coolify não manda o .git no contexto de build
— COPY .git/HEAD falha com "/.git/HEAD": not found e quebra o deploy
(comprovado no central, jun/2026). Nem a negação .dockerignore (.git/* +
!.git/HEAD) resolve, porque o .git simplesmente não está no contexto.
Se você precisa do SHA na imagem, o caminho é o Build Argument: marque
SOURCE_COMMIT como Available at Buildtime no Coolify (mesma mecânica da seção
acima). Sem isso, o ARG SOURCE_COMMIT=dev carimba dev — cosmético, não é
exigência da constituição (o contrato de runtime é a versão legível, não o
SHA — versionamento).
Dockerfile que lê docs/app.json: case a exceção no .dockerignore¶
Se o Dockerfile lê docs/app.json no build — a ponte de config da
Central de Apps: Flutter injeta o
client-grade via jq … docs/app.json → --dart-define, e um servidor pode
copiar docs/app.json para o classpath e lê-lo em runtime — lembre que o
.dockerignore tipicamente exclui docs/ (é peso morto para compilar).
Sem a exceção, o arquivo não entra no contexto de build: o jq/COPY
falha ou lê versão velha e o deploy quebra. As duas metades são uma decisão
só e viajam no mesmo PR — a ponte no Dockerfile e a exceção no
.dockerignore:
docs
!docs/app.json
Ao contrário do .git/HEAD acima, aqui a negação resolve: docs/app.json é
committado e está no contexto — é só o .dockerignore que o exclui. (Lá o
.git nem chega ao contexto do Coolify, então !.git/HEAD não adianta.)
Banco no PostgreSQL compartilhado¶
Criar banco e usuário dedicados por app, dentro do postgresql18
(terminal do Coolify ou psql):
CREATE DATABASE meu_app;
CREATE USER meu_app WITH PASSWORD 'senha-forte';
GRANT ALL PRIVILEGES ON DATABASE meu_app TO meu_app;
\c meu_app
GRANT ALL ON SCHEMA public TO meu_app; -- PostgreSQL 15+
Apps que usam PowerSync com "nuke" da replicação precisam de
ALTER ROLE meu_app WITH REPLICATION; (não dá superuser; só gerencia slots
de replicação).
API do Coolify de dentro de um container¶
Quando um app que roda no mesmo daemon Docker do Coolify precisa chamar a
API do Coolify, usar a URL interna http://coolify:8080 (DNS da network
Docker). A URL pública falha em produção: o certificado wildcard pode não
estar na truststore da JVM (PKIX path building failed) e o hairpin NAT cai
fora da allowlist (HTTP 403). A URL pública fica só para dev local.
Setar env/secret programaticamente (guard de deploy da Central de Apps)¶
A API expõe upsert em lote de variáveis por aplicação —
PATCH /api/v1/applications/{uuid}/envs/bulk (Bearer token; POST .../envs cria uma,
PATCH .../envs atualiza uma). "Secret" não é um tipo à parte: é uma env marcada
como Secret (fica oculta em logs/UI). Cada variável tem os flags Build Variable e
Runtime Variable (ambos ligados por default) — valor que o build embarca
(--dart-define, ARG) exige Build Variable ligado (ver
Build Arguments acima). É esse
endpoint que o guard de deploy da Central de Apps
usa para injetar os secrets de build no recurso; o payload exato é confirmado na
implementação do broker no auth. Ref: PATCH Update Envs (Bulk).
Atualizar, rollback e logs¶
- Deploy automático — dois modelos, escolha declarada por repo:
(a) auto-deploy no push (webhook de repo do Forgejo, evento Push) — todo push
na branch rebuilda, mesmo com o CI vermelho (o único gate é o próprio build
do Dockerfile); o aviso de falha do CI vem do webhook nativo da org no Telegram
(Forgejo ≥12, fora do YAML — ver Forgejo). (b) deploy gated pelo
CI — auto-deploy desligado no painel e o workflow ganha um job
deploy(needs: validar, só em push de master) que dispara o webhook de deploy do Coolify via curl comCOOLIFY_WEBHOOK_URL/COOLIFY_API_TOKEN(secrets do repo); CI vermelho = site/app antigo continua no ar. O central de docs usa (a): a faixa 3 (2026-07-05) chegou a adotar (b), mas foi revertido em 2026-07-07 por falta da config (secrets + auto-deploy off) — (b) fica como melhoria futura (o job comentado está nobuild-site.ymldo central; ver o backlog). - Manual: botão
Redeployno painel do recurso. - Rollback:
Deployments→ escolher revisão anterior →Rollback(o Coolify guarda as últimas imagens). - Logs: painel
Logsdo recurso (stdout/stderr do container); logs de aplicação persistidos ficam no volume mapeado.
Checklist pré-go-live¶
- [ ] Banco e usuário criados no
postgresql18 - [ ] URL do banco apontando para o hostname interno (não localhost/IP)
- [ ] DNS do domínio apontando para o servidor Coolify
- [ ] Variáveis configuradas; segredos marcados como Secret
- [ ] Valores de build (
--dart-define,ARG) como Build Argument; runtime como Environment Variable (não trocar os lados) - [ ] Volume de dados/logs mapeado
- [ ] Health check verde
- [ ] Teste manual do endpoint público (
curl -I https://.../health)