docker-compose.local.yml
Rama main · réplica de lectura del repositorio original.
# Stack local completa para desenvolvimento e VM Ubuntu.
#
# O Supabase local é gerenciado pelo `supabase start` e roda em Docker com os
# serviços oficiais de Postgres, Auth, PostgREST, Storage e Realtime. Este
# arquivo sobe os serviços da aplicação na mesma máquina, usando as portas
# publicadas pelo Supabase local.
#
# Uso: bash scripts/local-stack.sh up
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
APP_VERSION: ${APP_VERSION:-local}
image: deskcomm-app:local
pull_policy: never
restart: unless-stopped
env_file: ${LOCAL_ENV_FILE:-.env.local}
environment:
WAHA_API_BASE_URL: http://waha:3000
# O `worker` abaixo drena o event_log; ver docker-compose.prod.yml.
EVENT_LOG_WORKER_DRAINS: ${EVENT_LOG_WORKER_DRAINS:-true}
ports:
- "${APP_PORT:-3000}:3000"
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
waha:
condition: service_healthy
srh:
condition: service_started
healthcheck:
test: ["CMD", "node", "-e", "require('net').connect(3000,'127.0.0.1').on('connect',()=>process.exit(0)).on('error',()=>process.exit(1))"]
interval: 30s
timeout: 5s
retries: 5
start_period: 40s
worker:
build:
context: .
dockerfile: Dockerfile.worker
args:
APP_VERSION: ${APP_VERSION:-local}
image: deskcomm-worker:local
pull_policy: never
restart: unless-stopped
env_file: ${LOCAL_ENV_FILE:-.env.local}
environment:
WAHA_API_BASE_URL: http://waha:3000
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
waha:
condition: service_healthy
ports:
# Só 127.0.0.1: /healthz e /metrics do worker não têm autenticação e o
# /metrics conta filas, sessões e custo de todas as empresas. Sem endereço
# de bind o Docker publica em 0.0.0.0 — qualquer um na mesma rede (Wi-Fi
# do café, LAN do escritório) lia os números. O healthcheck roda DENTRO do
# container e não depende desta linha.
- "127.0.0.1:8787:8787"
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:8787/healthz"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
scheduler:
build:
context: .
dockerfile: Dockerfile.scheduler
args:
APP_VERSION: ${APP_VERSION:-local}
image: deskcomm-scheduler:local
pull_policy: never
restart: unless-stopped
env_file: ${LOCAL_ENV_FILE:-.env.local}
environment:
TZ: UTC
# Os crons autenticam com INTERNAL_CRON_SECRET quando definido; o app
# aplica a mesma prioridade. Sem este fallback, o scheduler enviava
# INTERNAL_SECRET e recebia 401 em instalações com os dois segredos.
INTERNAL_SECRET: ${INTERNAL_CRON_SECRET:-${INTERNAL_SECRET}}
depends_on:
app:
condition: service_healthy
waha:
image: devlikeapro/waha:latest-2026.7.2
# `platform` FIXADO por medição, não por hábito (#3032).
#
# O índice da tag acima publica UM arquivo só: `linux/amd64`. Medido em
# 2026-10-23 com `docker manifest inspect` sobre essa tag:
#
# [{"architecture":"amd64","os":"linux"},
# {"architecture":"unknown","os":"unknown"}]
#
# A tag irmã, a de arm64, é o espelho disso — só `linux/arm64`. Nenhuma das
# duas é multi-arch, então "trocar por uma tag multi-arch" não existe para
# esta versão: o que resta é declarar aqui a arquitetura que a tag publica.
#
# Sem esta linha o Compose pede o manifesto da arquitetura DO HOST, e num
# host arm64 (Mac Apple Silicon, VM arm64) o `up` morre em
#
# no matching manifest for linux/arm64/v8 in the manifest list entries
#
# — a stack local não sobe. Com a arquitetura declarada a resolução vale em
# qualquer host: em arm64 o contêiner roda por emulação (Docker Desktop traz
# Rosetta/qemu; numa VM arm64, o qemu-user estático registrado no binfmt).
# É a mesma escolha que o `docker-compose.yml` da raiz já faz para a mesma
# imagem, e o teste `tests/unit/plataforma-do-canal-no-compose-local.test.ts`
# guarda que ela não suma.
#
# O `docker-compose.prod.yml` NÃO fixa plataforma de propósito: lá a imagem
# vem de interpolação e o instalador grava a variante nativa da arquitetura
# do host (arm64 recebe a tag publicada só para arm64) — fixar amd64 ali
# quebraria o host arm64, e o pedido voltaria a não casar manifesto.
platform: linux/amd64
restart: unless-stopped
ports:
# 127.0.0.1 e não 0.0.0.0: o dashboard do WAHA está ligado logo abaixo e
# comanda a sessão de WhatsApp — ele tem autenticação própria, com padrão
# do WAHA, que a chave da API não protege. Sem endereço de bind, qualquer
# máquina da rede da VM alcançaria esse painel. `docker-compose.prod.yml`
# resolve o mesmo problema desligando o dashboard ("nunca expor o
# dashboard num template público", linha 156) e não publicando porta
# nenhuma para o WAHA; aqui ele fica de pé para quem desenvolve, mas só
# atende de dentro da própria máquina (de fora, `ssh -L 3030:localhost:3030`).
- "127.0.0.1:3030:3000"
environment:
# O WAHA recebe o hash com o prefixo para hashear a chave plaintext
# enviada pelo app em X-Api-Key.
WAHA_API_KEY: "sha512:${WAHA_API_KEY_SHA512}"
WHATSAPP_HOOK_URL: http://host.docker.internal:${APP_PORT:-3000}/api/v1/webhooks/waha
# `message.reaction` entra: é por onde chega o 👍/😐 do cliente (#2797).
# Mantido IGUAL ao `docker-compose.prod.yml` — o consumidor local de quem
# desenvolve não pode ver menos eventos que a instalação real.
WHATSAPP_HOOK_EVENTS: "message.any,message.ack,message.edited,message.revoked,message.reaction,session.status,state.change"
# Nome EXATO da doc do WAHA (events#hmac-authentication): sem o _KEY ele ignora.
WHATSAPP_HOOK_HMAC_KEY: ${WAHA_HMAC_SECRET}
WHATSAPP_DEFAULT_ENGINE: NOWEB
WAHA_DASHBOARD_ENABLED: "true"
WHATSAPP_RESTART_ALL_SESSIONS: "True"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- waha-data:/app/.sessions
- waha-media:/app/.media
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:3000/ping"]
interval: 30s
timeout: 5s
retries: 5
start_period: 40s
redis:
image: redis:7-alpine
restart: unless-stopped
command: ["redis-server", "--save", "", "--appendonly", "no"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 3s
retries: 3
srh:
image: hiett/serverless-redis-http@sha256:5b0bb9239fce53abf87b2018a7a0deb9ec7bd900c5360738fe5fbeeb426f9150
restart: unless-stopped
environment:
SRH_MODE: env
SRH_TOKEN: ${SRH_TOKEN}
SRH_CONNECTION_STRING: redis://redis:6379
depends_on:
redis:
condition: service_healthy
volumes:
waha-data:
waha-media: