Pular para o conteúdo principal

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.

Este guia descreve 4 serviços; só 3 têm Dockerfile pronto hoje

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

  1. 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).
  2. Build: método = Dockerfile, usando o Dockerfile multi-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) e app (imagem de runtime, com Chromium instalado via apt-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ávelValor / observação
PORT8080
AUTHENTICATION_API_KEYObrigatória. Chave forte e secreta, exigida no header apikey.
DATABASE_URLObrigatória. String de conexão do PostgreSQL da stack (serviço 2).
REDIS_URLObrigatória. String de conexão do Redis da stack (serviço 3).
WEBHOOK_GLOBAL_URLOpcional — webhook padrão para instâncias sem webhook próprio.
MAX_INSTANCESQuantidade máxima de instâncias simultâneas — ver dimensionamento de RAM abaixo.
LOG_LEVELinfo
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).
TZAmerica/Cuiaba (ou o timezone padrão desejado para as janelas de anti-ban).
CORS_ORIGINOrigem(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_SECRETObrigató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_PASSWORDUsadas 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).

Sem este volume, toda reinicialização perde os pareamentos

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 Dockerfile já define um HEALTHCHECK interno:

    HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=180s \
    CMD curl -fsS http://localhost:${PORT:-8080}/health || exit 1

    O --start-period=180s existe porque, na subida, o processo Node restaura em segundo plano até MAX_INSTANCES sessões de Chromium (restaurarInstancias, src/index.ts) — o endpoint /health já responde assim que o listen sobe, 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 mesmo GET /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 containerMAX_INSTANCES sugerido
~1GB3–5
~2GB5–8
~4GB12–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