Deploy no EasyPanel
Como levar a stack para produção em uma VPS com EasyPanel, buildando a
partir do Dockerfile do repositório GitHub — o mesmo padrão usado nos
demais apps da e2a.
A stack completa tem quatro serviços: a API (que também serve o
Portal de Controle em /panel), PostgreSQL, Redis e este
site de documentação. Os três primeiros já têm Dockerfile/docker-compose.yml
neste repositório e o processo abaixo é o real, testado localmente. O
Dockerfile próprio do site de documentação (docs-site/Dockerfile) ainda
não existe — está planejado para uma task futura (build do Docusaurus →
Nginx servindo o estático). A seção Serviço 4 — este site
descreve a topologia pretendida; trate-a como não validada até esse
Dockerfile existir.
Serviço 1 — a API (+ Portal de Controle)
Source e build
- No EasyPanel, crie um serviço do tipo App, aponte para o repositório
GitHub deste projeto e selecione o branch a ser observado (ex.:
main). - Build: método = Dockerfile, usando o
Dockerfilemulti-stage na raiz do repositório. Ele tem três estágios:build(compila o TypeScript e gera o client do Prisma),panel(builda a SPA React do portal,panel/→panel/dist) eapp(imagem de runtime, com Chromium instalado viaapt-get, que copia os artefatos dos dois estágios anteriores).
Variáveis de ambiente
Configure no painel do EasyPanel (nunca commitar .env no repositório) —
lista completa, com o que cada uma faz:
| Variável | Valor / observação |
|---|---|
PORT | 8080 |
AUTHENTICATION_API_KEY | Obrigatória. Chave forte e secreta, exigida no header apikey. |
DATABASE_URL | Obrigatória. String de conexão do PostgreSQL da stack (serviço 2). |
REDIS_URL | Obrigatória. String de conexão do Redis da stack (serviço 3). |
WEBHOOK_GLOBAL_URL | Opcional — webhook padrão para instâncias sem webhook próprio. |
MAX_INSTANCES | Quantidade máxima de instâncias simultâneas — ver dimensionamento de RAM abaixo. |
LOG_LEVEL | info |
PUPPETEER_EXECUTABLE_PATH | /usr/bin/chromium (já é o padrão definido na imagem). |
SESSIONS_PATH | /app/sessions (já é o padrão da imagem — precisa bater com o ponto de montagem do volume, ver abaixo). |
TZ | America/Cuiaba (ou o timezone padrão desejado para as janelas de anti-ban). |
CORS_ORIGIN | Origem(ns) permitida(s) para CORS, separadas por vírgula. Em produção, restrinja à origem real do portal (ex.: https://portal.exemplo.com.br) em vez do * (default). |
JWT_SECRET | Obrigatória. Segredo de assinatura dos JWT de sessão do portal, mínimo 16 caracteres. A API não sobe sem essa variável (validação no boot, src/infra/config.ts). Gere um valor longo e aleatório, nunca reaproveitado entre ambientes. |
ADMIN_EMAIL / ADMIN_PASSWORD | Usadas apenas para semear o primeiro usuário administrador do portal — o seeder só roda se a tabela de usuários estiver vazia. Depois que existir ao menos um usuário, essas variáveis deixam de ter efeito; podem ser removidas do ambiente após o primeiro boot em produção. Ver Portal de Controle. |
Volume persistente — /app/sessions
Monte um volume persistente em /app/sessions (o caminho padrão da
imagem, também configurável via SESSIONS_PATH).
As sessões autenticadas do WhatsApp (o resultado de escanear o QR Code)
ficam salvas nesse diretório. Se ele não for montado como volume
persistente, cada redeploy ou restart do container derruba todas as
instâncias conectadas e será preciso escanear o QR Code de novo, uma a
uma. Isso vale tanto para o volume do EasyPanel em produção quanto para o
volume sessions:/app/sessions do docker-compose.yml local.
Também vale montar um volume em /app/data para dados auxiliares locais,
mas o impacto de não montá-lo é menor do que o de /app/sessions.
Porta e healthcheck
-
Porta exposta:
8080— a imagem expõe essa porta; aponte o proxy/domínio do EasyPanel para ela. -
Healthcheck: o
Dockerfilejá define umHEALTHCHECKinterno:HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=180s \CMD curl -fsS http://localhost:${PORT:-8080}/health || exit 1O
--start-period=180sexiste porque, na subida, o processo Node restaura em segundo plano atéMAX_INSTANCESsessões de Chromium (restaurarInstancias,src/index.ts) — o endpoint/healthjá responde assim que olistensobe, antes dessa restauração terminar, mas os 180s dão folga extra para não deixar reinícios em cascata em ambientes mais lentos. Configure o healthcheck do próprio EasyPanel (se disponível na versão usada) para bater no mesmoGET /health, com uma tolerância de partida equivalente.
Migrations e boot
O CMD da imagem roda npx prisma migrate deploy automaticamente antes de
iniciar o processo Node — não é necessário rodar migrations manualmente em
produção. Se DATABASE_URL/REDIS_URL estiverem incorretos ou os serviços
ainda não estiverem no ar, o container falha ao iniciar.
Dimensionamento de RAM vs MAX_INSTANCES
Cada instância conectada mantém seu próprio processo Chromium headless,
consumindo em torno de 150–300MB de RAM por instância (varia com o
volume de mensagens e mídia). Dimensione MAX_INSTANCES de acordo com a
memória disponível no container/VPS:
| RAM disponível para o container | MAX_INSTANCES sugerido |
|---|---|
| ~1GB | 3–5 |
| ~2GB | 5–8 |
| ~4GB | 12–18 |
Ultrapassar o limite de memória sem ajustar MAX_INSTANCES pode derrubar o
processo por OOM (out of memory) e causar perda de conexão de todas as
instâncias — não só das mais recentes.
Serviço 2 — PostgreSQL
Suba um serviço PostgreSQL na stack do EasyPanel (ou aponte para um
PostgreSQL externo acessível pela rede do container). O docker-compose.yml
local usa postgres:16-alpine como referência de versão. Aponte
DATABASE_URL do serviço 1 para essa instância.
Serviço 3 — Redis
Suba um serviço Redis na stack (ou externo). O docker-compose.yml local
usa redis:7-alpine como referência. Aponte REDIS_URL do serviço 1 para
essa instância. O Redis é usado pelas filas BullMQ (envio de mensagens
proativas, espaçamento entre envios).
PostgreSQL e Redis precisam existir e estar acessíveis antes de subir a API — a aplicação não sobe sem os dois.
Serviço 4 — site de documentação
A intenção é que este próprio site (docs-site/) rode como um quarto
serviço na mesma stack, com:
- Source: o mesmo repositório GitHub, mesmo branch.
- Build context:
docs-site/(não a raiz do repositório). - Subdomínio próprio (ex.:
docs.exemplo.com), distinto do domínio da API/portal.
Como observado no aviso no topo desta página, o docs-site/Dockerfile
ainda não existe neste repositório — quando existir, o padrão esperado é
multi-stage (build do Docusaurus → Nginx servindo o build/ estático),
seguindo o mesmo modelo dos demais apps da e2a. Até lá, este serviço não
pode ser configurado no EasyPanel como descrito.
Cada push dispara um novo build
Uma vez configurada a stack, cada push no branch observado pelo EasyPanel dispara um novo build automático — não há passo manual de deploy além de configurar o serviço a primeira vez.
Próximos passos
- Instalação local — rodar a mesma stack na sua máquina antes de ir para produção.
- Portal de Controle — telas, papéis e o primeiro acesso depois do deploy.