Bootstrap do servidor
Primeira instalação do SmartPerformance SFA num servidor zerado: bootstrap.sh. Decisões em
ADR-045 (.claude/docs/decisions.md).
| Situação | Script |
|---|---|
Servidor novo, sem .env, sem nada em 80/443 | bootstrap.sh |
| Instalação que já existe, subindo uma versão nova | deploy.sh |
O bootstrap.sh é idempotente: rodar de novo não regenera segredo já definido nem reemite
certificado válido. É para ser rodado outra vez depois de corrigir um DNS ou liberar a porta 80.
Pré-requisitos
- Docker Engine + plugin
docker compose(v2) eopenssl; - portas 80 e 443 livres no host e liberadas no firewall da nuvem;
- registros A dos três domínios apontando para o servidor.
Uso
git clone <repo> && cd SmartPerformanceSfa
bash bootstrap.sh --email voce@empresa.com \
--app sfa.exemplo.com \
--api api.sfa.exemplo.com \
--docs docs.sfa.exemplo.com
Você não precisa editar o .env antes. A precedência é argumento → .env → pergunta: sem
argumentos ele pergunta o que faltar e grava no .env; na segunda execução lê tudo de lá e não
pergunta nada. Valor de exemplo (*.exemplo.com, change_me…) conta como não preenchido — com
um domínio de exemplo herdado, o certbot tentaria provar um domínio que não é seu.
Sem terminal para perguntar (ssh host 'bash bootstrap.sh', CI), passe tudo por argumento — o
script avisa em vez de falhar no read.
| Opção | Para quê |
|---|---|
--staging | Ensaio com o ambiente de teste do Let's Encrypt — não gasta o limite de emissões. |
--sem-tls | Rede interna/homologação sem domínio público: HTTP puro, sem certificado. |
--sem-build | Pula o build das imagens (já construídas ou vindas do registry). |
O que ele faz
- Pré-requisitos — Docker, compose v2, openssl; avisa se 80/443 já estão ocupadas.
.env— cria a partir do.env.examplee gera os segredos:POSTGRES_PASSWORD,JWT_ACCESS_SECRET,JWT_REFRESH_SECRET,INTEGRATION_SECRET_KEY. GravaNODE_ENV=production,BIND_ADDR=127.0.0.1e as URLs internas do compose (postgres,redis,api).- Domínios — grava
DOMAIN_*,WEB_ORIGINeNEXT_PUBLIC_DOCS_URL; confere o DNS (aviso, não erro — atrás de CDN o A record não é o IP do servidor). - Stack — build, sobe
postgres/redis, espera o banco e sobeapi/web/docs. As migrations e o seed rodam no próprio CMD da imagem da API. - nginx (fase 1) — só a porta 80, servindo o desafio ACME.
- certbot — um certificado com os três nomes.
- nginx (fase 2) — vhosts TLS, HTTP passa a redirecionar, e a renovação automática sobe.
- Conferência —
curlnos três endereços.
Por que duas fases
Um vhost TLS apontando para um certificado que ainda não existe impede o nginx de subir; e sem
nginx no ar o certbot não tem como provar o domínio. A fase 1 quebra esse ciclo servindo apenas
/.well-known/acme-challenge/ em HTTP.
Arquivos
bootstrap.sh o script
deploy/lib/bootstrap.sh biblioteca compartilhada (cópia — ver deploy/lib/README.md)
docker-compose.prod.yml nginx + certbot (some ao docker-compose.yml)
deploy/nginx/templates/ http.conf · tls.conf · plain.conf (com campos a substituir)
deploy/nginx/includes/ proxy.inc + um site-*.inc por host
deploy/nginx/conf.d/ RENDERIZADO — fora do git
A biblioteca vem da skill server-bootstrap do project-template e carrega o que não vale a pena
redescobrir a cada projeto: .env como memória (com escape de & no sed), render que recusa
configuração com campo por substituir, --keep-until-expiring no certbot, conferência HTTP que não
mente. Como aqui a borda roda em container, o script redefine os ganchos certbot_cli,
nginx_test e nginx_reload — o padrão da biblioteca é falar com o nginx e o certbot do host.
Os includes guardam o que é específico de cada host: limite de corpo de 25 MB (molde da
organização, lotes de integração) e SSE sem buffer para a tela de Execuções — com buffer, o
"ao vivo" atualiza em rajadas ou nunca.