1. Visão geral — quem faz o quê
🧩 paas_form (addon Odoo)
Roda no apphub (Odoo). É a interface: o usuário preenche um formulário, faz upload de um .html/.zip e clica em Deploy. O addon então fala com a infra da VM por API.
- Cria o repositório e commita os arquivos no Gitea
- Cria a aplicação e dispara o deploy no Coolify
- Expõe o endpoint do gate (SSO/OTP) que o Traefik consulta
- Lê status, gerencia promoção staging→produção (PR)
🖥️ paas-devops (a VM)
Scripts bash idempotentes (steps/00…70) que provisionam tudo numa VM Ubuntu, do zero: Docker, Coolify, Gitea, acme-dns, gate e o wildcard TLS.
- Todas as imagens vêm de um mirror ghcr privado (nunca docker.io)
- Gitea interno (IP fixo), Coolify orquestra os builds/containers
- Wildcard
*.<suffix>via acme-dns esconde os nomes dos apps do Certificate Transparency make bootstrap/make update/make teardown
2. 🗺️ Mapa da arquitetura
Os números n nas setas correspondem à legenda de ações abaixo do desenho.
Legenda de ações
https://<slug>-staging.<suffix>.forwardAuth consulta /paas_form/gate/verify (SSO/OTP) antes de liberar o app.atlasren/<slug> e commita site/ + Dockerfile + docker-compose.yml.git@10.0.x.250 (chave SSH / deploy key).docker build FROM ghcr.io/.../static-web + COPY site.web-<uuid> na rede coolify.*.<suffix> (DNS-01), grava o TXT no acme-dns._acme-challenge.<suffix> é um CNAME para a zona do acme-dns.make deploy (rsync+SSH) ou sudo -E make bootstrap na VM.3. Fluxo de um deploy (staging)
index.html/.zip e clica Deploy staging.atlasren/<slug>, garante a branch staging e commita site/* + Dockerfile (FROM ghcr static-web) + docker-compose.yml.trigger_deploy na staging.git@10.0.x.250), faz docker build (puxa a base do ghcr) e sobe o container web-<uuid>.forwardAuth (gate) + TLS. Com o wildcard ativo, o router usa tls:{} (sem cert per-app → nada vaza no Certificate Transparency).https://<slug>-staging.<suffix> → Traefik → gate (SSO/OTP no Odoo) → app servindo o HTML.<slug>.<suffix>).4. Tutorial — aplicar na VM Azure (PaasMachine), passo a passo
4.1 Pré-requisitos
| Item | Detalhe |
|---|---|
| VM | Ubuntu 24.04, x86/amd64 (o mirror ghcr é amd64). Azure PaasMachine = 20.106.196.125, user paasuser, SSH porta 31847. |
| Portas (NSG Azure) | liberar 22/SSH, 80, 443 e 53 (UDP+TCP) para o acme-dns (origem Any — o LE valida de IPs variáveis). |
| PAT GitHub | token só com read:packages → o daemon loga no ghcr e puxa o mirror privado. |
| Domínio + DNS | controle do DNS de atlasren.com (GoDaddy ou Cloudflare) para criar A wildcard + os 3 registros do acme-dns. |
| Egress | Azure NSG fechado → setar EGRESS_PROXY_URL=http://10.3.0.28:3128 (Squid). Se aberto → deixar vazio. |
4.2 Preparar a VM
# liberar a porta 53 do systemd-resolved (acme-dns usa a 53)
sudo sed -i 's/^#\?DNSStubListener=.*/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
4.3 Preencher o config.env
Na sua máquina, no repo paas-devops: cp config.env.example config.env e edite (ver tabela completa de variáveis). Exemplo de produção (Azure):
PROFILE=prod DOMAIN_SUFFIX=paas.atlasren.com COOLIFY_DOMAIN=coolify.paas.atlasren.com GITEA_DOMAIN=gitea.paas.atlasren.com SSH_PORT=31847 ACME_EMAIL=devops@atlasren.com COOLIFY_VERSION=4.1.2 COOLIFY_ADMIN_EMAIL=admin@atlasren.com COOLIFY_ADMIN_PASSWORD=<senha-forte> EGRESS_PROXY_URL=http://10.3.0.28:3128 # Azure egress fechado (vazio se aberto) WILDCARD_TLS_ENABLED=1 ACME_DNS_ZONE=acme.atlasren.com VM_PUBLIC_IP=20.106.196.125 ACME_CA_SERVER=https://acme-staging-v02.api.letsencrypt.org/directory # staging no 1º teste GHCR_NS=ghcr.io/atlasren-partners GHCR_USER=<login-github> GHCR_TOKEN=<PAT read:packages> GITEA_ORG=atlasren GITEA_ADMIN_USER=gitea_admin GITEA_ADMIN_PASS=<senha-forte> GITEA_BOT_USER=paas-bot GITEA_BOT_PASS=<senha-forte> GITEA_HTTP_PUBLISH_PORT= # VAZIO em prod (gitea interno). 3000 só p/ teste por IP GATE_VERIFY_URL=https://apphub-staging.atlasren.com/paas_form/gate/verify COOLIFY_SSO_AZURE_CLIENT_ID=<...> # SSO opcional no Coolify COOLIFY_SSO_AZURE_CLIENT_SECRET=<...> COOLIFY_SSO_AZURE_TENANT=<...>
step 10 (instalador do Coolify) puxa postgres/redis/traefik do docker.io no primeiro install (antes do step 15 re-apontar). Em uma VM já viva, prefira make deploy STEP=NN.4.4 Rodar o bootstrap
# da sua máquina (rsync do repo + roda na VM por SSH): make deploy # bootstrap completo na VM make deploy STEP=15 # só um step (em janela) # OU diretamente na VM: sudo -E make bootstrap
Ordem dos steps: 00 vm-prep · 05 egress (direto ou Squid) · 10 Coolify (+setup+proxy) · 15 re-point infra→ghcr · 20 Coolify config (admin, token, fqdn, SSO) · 30 Gitea · 40 Gitea config (admin/bot/deploy-key) · 50 gate · 55 wildcard (acme-dns) · 60 dns/tls · 70 credenciais.
4.5 DNS — os registros
Base (uma vez): A *.paas → IP (e A coolify, A gitea se não usar wildcard de 1 nível). O step 55 imprime os 3 registros do acme-dns — crie no DNS:
| Tipo | Nome | Conteúdo |
|---|---|---|
| A | acme | IP da VM |
| NS | acme | acme.atlasren.com (delega a subzona) |
| CNAME | _acme-challenge.paas | <fulldomain>.acme.atlasren.com (o step 55 imprime) |
4.6 Emitir o wildcard
O wildcard-sync.timer re-tenta sozinho quando o CNAME propaga. Para validar e depois trocar pra produção:
sudo systemctl start wildcard-sync.service sudo openssl x509 -in /data/coolify/proxy/certs/wildcard.crt -noout -ext subjectAltName # DNS:*.paas... # validado no staging → trocar ACME_CA_SERVER= (vazio) no config.env e re-rodar step 55
4.7 Verificação
- Coolify:
https://coolify.paas.atlasren.com→ loginCOOLIFY_ADMIN_EMAIL/ senha. (ouhttp://<IP>:8000) - Gitea:
https://gitea.paas.atlasren.com→gitea_admin/GITEA_ADMIN_PASS. - Deploy de teste pelo paas_form → app servindo em
<slug>-staging.paas.atlasren.com.
5. config.env — todas as variáveis de entrada
| Variável | O quê | Exemplo / prod |
|---|---|---|
PROFILE | prod (LE, domínios reais) ou test (DinD, nip.io) | prod |
DOMAIN_SUFFIX | sufixo dos apps; injetado no gate | paas.atlasren.com |
COOLIFY_DOMAIN / GITEA_DOMAIN | FQDN do dashboard Coolify / do Gitea | coolify.paas… / gitea.paas… |
SSH_PORT · ACME_EMAIL | porta SSH (UFW) · conta LE (lego) | 31847 · devops@… |
COOLIFY_VERSION | pinar (:latest muda API/proxy) | 4.1.2 |
COOLIFY_ADMIN_EMAIL / _PASSWORD | admin logável do Coolify (UI) | login |
EGRESS_PROXY_URL | vazio=direto · setado=Squid (Azure fechado) | http://10.3.0.28:3128 |
WILDCARD_TLS_ENABLED | 1 ativa o wildcard via acme-dns | 1 |
ACME_DNS_ZONE · VM_PUBLIC_IP | zona do acme-dns · IP público | acme.atlasren.com · IP |
ACME_CA_SERVER | vazio=LE prod · staging p/ testar | staging→vazio |
GHCR_NS / GHCR_USER / GHCR_TOKEN | mirror privado + PAT read:packages | ghcr.io/atlasren-partners |
GITEA_ORG · GITEA_ADMIN_* · GITEA_BOT_* | org + admin + bot (token do paas_form) | atlasren · login |
GITEA_HTTP_PUBLISH_PORT | vazio=interno (prod) · 3000=acesso por IP (teste) | vazio em prod |
GATE_VERIFY_URL | endpoint do gate no Odoo | https://apphub…/paas_form/gate/verify |
COOLIFY_SSO_AZURE_* | SSO Azure AD no Coolify (opcional) | client_id / secret / tenant |
O config.env é gitignored. Na VM vive em /opt/paas-devops/config.env (chmod 600). No CI ele viaja dentro do secret PAAS_CONFIG_ENV.
6. Saídas → onde configurar no Odoo (Settings → PaaS)
O step 70 imprime, e o arquivo paas-devops/out/credentials.env guarda, os valores gerados. Copie para Settings → PaaS do apphub (Odoo):
| Settings → PaaS (Odoo) | Vem de (credentials.env) | Modo domínio (prod) | Modo IP (teste) |
|---|---|---|---|
| Gitea URL | — | https://gitea.paas.atlasren.com | http://<IP>:3000 |
| Gitea URL (internal) | GITEA_URL_INTERNAL | git@10.0.x.250 (o Coolify clona — sempre interno) | |
| Gitea token | GITEA_API_TOKEN | token do paas-bot | |
| Gitea org | GITEA_ORG | atlasren | |
| Coolify URL | — | https://coolify.paas.atlasren.com | http://<IP>:8000 |
| Coolify token | COOLIFY_API_TOKEN | id|plain (tem |) | |
| Deploy Key UUID (SSH) | COOLIFY_PRIVATE_KEY_UUID | chave que o Coolify usa pra clonar o Gitea | |
| Default Project UUID | COOLIFY_PROJECT_UUID | projeto atlasren | |
| Default Server UUID | COOLIFY_SERVER_UUID | servidor localhost | |
| Domain suffix | DOMAIN_SUFFIX | paas.atlasren.com | |
get_param por processo) e dê Ctrl+Shift+R. A "Static base image" foi removida — a base é fixa no código (ghcr.io/.../static-web).7. Operação do dia a dia
🔄 Atualizar (preserva dados)
sudo make update — re-pull das imagens (Coolify + mirror) e up -d sem -v; re-aplica steps 15/30/40/50/55/60. Nunca toca em volumes/bancos. Pula installer e rotação de token (10/20).
🧹 Limpar tudo
sudo make teardown — remove Coolify/Gitea/acme-dns/apps/volumes/units (confirma digitando o hostname). ARGS=--purge-docker remove até o Docker+swap. Irreversível.
8. Especificidades da VM Azure (vs. a de teste)
| Aspecto | Azure (prod) | Teste (Contabo/Hetzner) |
|---|---|---|
| Egress | fechado no NSG → EGRESS_PROXY_URL=http://10.3.0.28:3128 (Squid) | aberto → EGRESS_PROXY_URL= vazio |
| Gitea | interno → GITEA_HTTP_PUBLISH_PORT= vazio (acesso só via domínio) | 3000 p/ acesso por IP (Odoo fora da VM) |
| TLS | LE produção (cert válido) → ACME_CA_SERVER= vazio | LE staging no 1º teste |
| Odoo | apphub real (apphub-staging.atlasren.com) aponta pros domínios https | Odoo dev (WSL) aponta pros IPs http |
| SSO Coolify | Azure AD (COOLIFY_SSO_AZURE_*) | — |
<slug>.atlasren.com aparece no Certificate Transparency (crt.sh) e scanners sondam. O wildcard *.paas.atlasren.com publica só o coringa — os nomes dos apps nunca vazam.Gerado a partir do estado validado em VM real · repos atlasren-partners/paas-devops + paas-form · branch feat/offline-coolify-deploy.