Ctrl K
ComunidadGuías…

docker-compose.local.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 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: