docker-compose.traefik.yml
Rama main · réplica de lectura del repositorio original.
# DeskcommCRM — override para VPS que JÁ TEM um proxy reverso Traefik.
#
# docker compose -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d
#
# QUANDO USAR
# -----------
# O stack padrão sobe um Caddy que publica as portas 80 e 443. Isso é o certo
# num VPS "cru". Mas várias hospedagens entregam a VPS com um Traefik próprio
# já ocupando essas portas — Hostinger, Coolify, Dokploy, CapRover e afins. É
# esse Traefik que dá HTTPS automático a tudo que o painel instala; desligá-lo
# quebraria as automações da hospedagem, presentes e futuras.
#
# Dois processos não podem ouvir a mesma porta, então o `up -d` padrão falha no
# bind e a instalação morre no meio. Com este override:
#
# 1. o serviço `caddy` ganha um profile que nunca é ativado — o compose
# simplesmente não o cria (o Traefik da hospedagem faz o papel dele);
# 2. o serviço `app` ganha labels do Traefik que reproduzem, uma a uma, as
# regras do Caddyfile.
#
# O comportamento padrão (sem este arquivo) fica EXATAMENTE como antes: quem
# não passa o `-f docker-compose.traefik.yml` não é afetado por nada aqui.
#
# EQUIVALÊNCIA COM O Caddyfile
# ----------------------------
# - `reverse_proxy app:3000` → router `deskcomm` + loadbalancer.server.port
# - `encode gzip` → middleware `deskcomm-compress`
# - `respond @waha_global 403`→ router `deskcomm-waha-block` (ver abaixo)
#
# PRÉ-REQUISITOS NO TRAEFIK DA HOSPEDAGEM
# ---------------------------------------
# Espera-se um entrypoint `websecure` (443) e um certresolver `letsencrypt` —
# os nomes padrão dessas instalações. Se a sua usa outros nomes, ajuste
# TRAEFIK_ENTRYPOINT e TRAEFIK_CERTRESOLVER no .env.
#
# A REDE onde o Traefik encontra o app é TRAEFIK_NETWORK, e ela varia por
# instalação. O install.sh a descobre e grava no .env — são dois cenários com
# respostas OPOSTAS, ambos medidos (o porquê de cada um está na função
# `rede_do_traefik`, em hostgator-setup-kit/install.sh):
#
# Traefik numa bridge PRÓPRIA (Coolify, Dokploy) → a rede DELE, que já existe.
# Traefik em `--network host` (Hostinger) → uma bridge "<projeto>_proxy"
# que o install.sh CRIA. Em modo host o proxy não está em rede nenhuma do
# Docker, e alcança qualquer bridge pela stack do host.
#
# O default abaixo ("traefik") cobre a instalação manual mais comum.
services:
app:
# Só o `app` entra na rede do proxy. O inverso — juntar o Traefik à rede
# `internal` — colocaria um contêiner que serve OUTROS tenants da VPS dentro
# de uma rede onde o redis roda sem senha (docker-compose.prod.yml: `redis-server
# --save '' --appendonly no`, sem requirepass). waha, redis e srh ficam de fora.
networks:
- internal
- proxy
labels:
traefik.enable: "true"
# Este label diz ao Traefik em qual rede pegar o IP do contêiner. Com o
# Traefik numa bridge própria, é a rede do PROXY — apontando para a
# `internal` ele mira um IP que não alcança e a requisição pendura até dar
# timeout, mesmo com o contêiner já conectado nas duas redes. Medido com
# Traefik v3.3 real:
# app só na internal, label=internal -> HTTP 000 (timeout)
# app nas duas redes, label=internal -> HTTP 000 (ainda o IP errado)
# app nas duas redes, label=proxy -> HTTP 200
# Com o Traefik em modo host a conclusão não se inverte, ela se generaliza:
# continua sendo a rede `proxy` — só que lá ela é a bridge que o install.sh
# cria para este projeto, porque um proxy em modo host não tem rede própria
# (e alcança qualquer bridge pela stack do host).
traefik.docker.network: "${TRAEFIK_NETWORK:-traefik}"
# Rota principal — equivale ao `reverse_proxy app:3000` do Caddyfile.
traefik.http.routers.deskcomm.rule: "Host(`${DOMAIN}`)"
traefik.http.routers.deskcomm.entrypoints: "${TRAEFIK_ENTRYPOINT:-websecure}"
traefik.http.routers.deskcomm.tls.certresolver: "${TRAEFIK_CERTRESOLVER:-letsencrypt}"
traefik.http.routers.deskcomm.middlewares: "deskcomm-compress"
traefik.http.services.deskcomm.loadbalancer.server.port: "3000"
# `encode gzip` do Caddyfile.
traefik.http.middlewares.deskcomm-compress.compress: "true"
# `respond @waha_global 403` do Caddyfile. O webhook GLOBAL do WAHA não
# pode ser alcançável da internet: quem soubesse o endereço injetaria
# mensagem falsa no CRM e faria o WhatsApp do dono responder a um número
# arbitrário. Aqui dentro o WAHA fala com o app pela rede do Docker, então
# esta rota nunca precisou ser pública.
#
# O Traefik não tem um "responda 403" direto. O equivalente é um
# ipallowlist cuja única faixa permitida é 192.0.2.1/32 — TEST-NET-1
# (RFC 5737), um endereço reservado para documentação que nenhum cliente
# real tem. Toda requisição àquele caminho cai fora da lista e recebe 403,
# que é exatamente o comportamento desejado. A prioridade alta é o que faz
# esta rota vencer a principal, que casaria o mesmo Host.
traefik.http.routers.deskcomm-waha-block.rule: "Host(`${DOMAIN}`) && Path(`/api/v1/webhooks/waha`)"
traefik.http.routers.deskcomm-waha-block.entrypoints: "${TRAEFIK_ENTRYPOINT:-websecure}"
traefik.http.routers.deskcomm-waha-block.tls.certresolver: "${TRAEFIK_CERTRESOLVER:-letsencrypt}"
traefik.http.routers.deskcomm-waha-block.priority: "1000"
traefik.http.routers.deskcomm-waha-block.service: "deskcomm"
traefik.http.routers.deskcomm-waha-block.middlewares: "deskcomm-deny"
traefik.http.middlewares.deskcomm-deny.ipallowlist.sourcerange: "192.0.2.1/32"
# HTTP -> HTTPS. O Caddy fazia isto automaticamente; no Traefik precisa ser
# dito. Sem este router, quem digitar http:// não recebe resposta nenhuma.
traefik.http.routers.deskcomm-http.rule: "Host(`${DOMAIN}`)"
traefik.http.routers.deskcomm-http.entrypoints: "${TRAEFIK_ENTRYPOINT_HTTP:-web}"
traefik.http.routers.deskcomm-http.middlewares: "deskcomm-https"
traefik.http.routers.deskcomm-http.service: "deskcomm"
traefik.http.middlewares.deskcomm-https.redirectscheme.scheme: "https"
traefik.http.middlewares.deskcomm-https.redirectscheme.permanent: "true"
# O Caddy não é removido do compose (isso exigiria editar o arquivo base e
# quebraria quem usa o padrão) — ele só é posto num profile que nunca é
# ligado. Sem profile ativo, o compose não cria o contêiner e as portas 80/443
# seguem com o Traefik da hospedagem.
#
# ATENÇÃO a um detalhe do Compose: nomear o serviço explicitamente
# (`docker compose up -d caddy`) ATIVA o profile dele automaticamente e o
# contêiner sobe assim mesmo, indo bater de frente com o Traefik. Por isso o
# update.sh checa REVERSE_PROXY antes de recriar o proxy.
caddy:
profiles: ["caddy-nao-usado-com-proxy-externo"]
# `external: true` = quem cria esta rede não é o compose. Nos dois cenários ela
# já existe quando o `up -d` roda: ou é a rede do proxy da hospedagem (coolify,
# dokploy-network, traefik...), ou é a bridge que o install.sh criou para este
# projeto quando o Traefik está em modo host.
#
# Rede externa AUSENTE é recusada ANTES de o compose criar qualquer coisa —
# medido com o compose v2.38.2 contra este mesmo arquivo: "network X declared as
# external, but could not be found", sem dizer de onde saiu o nome. Vale
# inclusive quando X é a `<projeto>_internal` que o próprio compose criaria neste
# mesmo `up` (a checagem de rede externa vem antes de qualquer criação); por isso
# a bridge do modo host é uma rede separada, e o install.sh a cria antes de subir
# a stack.
networks:
proxy:
external: true
name: "${TRAEFIK_NETWORK:-traefik}"