Deploy com Docker

O mxout é distribuído como uma imagem scratch com binário estático musl (~6 MB). A configuração e as chaves são montadas em runtime — nenhum segredo é embutido na imagem.

Pré-requisitos

  • Porta 25 de saída liberada no servidor (verifique com seu provedor).
  • Registro PTR (rDNS) configurado no IP do servidor apontando para o hostname de envio.
  • Kit DKIM gerado com mxout-keygen. Ver Configuração.

O que montar

Tudo que o mxout precisa fica em /etc/mxout/:

Caminho no container Conteúdo
/etc/mxout/mxout.json Arquivo de configuração principal.
/etc/mxout/kits/<dominio>.<selector>.key.pem Chave privada DKIM de cada domínio.

Monte esse diretório como read-only. O mxout não escreve nada em disco.

Variáveis de ambiente

Variável Obrigatória Descrição
MXOUT_AUTH_TOKEN Sim Token de autenticação das requisições HTTP. Trate como secret.
MXOUT_CONFIG Não Caminho do mxout.json. Padrão: /etc/mxout/mxout.json.
MXOUT_DNS Sim (scratch) Resolver DNS explícito (ex.: 1.1.1.1). Obrigatório porque a imagem scratch não tem /etc/resolv.conf.
RUST_LOG Não Verbosidade dos logs. Padrão: info.

Rodar com docker run

docker run -d \
  --name mxout \
  --restart unless-stopped \
  -p 8080:8080 \
  -v /etc/mxout:/etc/mxout:ro \
  -e MXOUT_AUTH_TOKEN=<TOKEN_SECRETO> \
  -e MXOUT_DNS=1.1.1.1 \
  -e RUST_LOG=info \
  ghcr.io/ccs-systems/mxout:latest

Verifique se o serviço está saudável:

curl http://localhost:8080/health

Resposta esperada: ok

Rodar com docker compose

services:
  mxout:
    image: ghcr.io/ccs-systems/mxout:latest
    container_name: mxout
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - /etc/mxout:/etc/mxout:ro
    environment:
      MXOUT_AUTH_TOKEN: "${MXOUT_AUTH_TOKEN}"
      MXOUT_DNS: "1.1.1.1"
      RUST_LOG: "info"
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
    labels:
      - "ccs.systems/project=mxout"
      - "ccs.systems/type=sys"
      - "ccs.systems/env=prod"
      - "traefik.enable=true"
      - "traefik.http.routers.mxout.rule=Host(`mxout.exemplo.com`)"
      - "traefik.http.routers.mxout.entrypoints=websecure"
      - "traefik.http.routers.mxout.tls.certresolver=myresolver"
      - "traefik.http.services.mxout.loadbalancer.server.port=8080"
    networks:
      - public

networks:
  public:
    external: true

Armazene MXOUT_AUTH_TOKEN no .env (nunca no docker-compose.yml):

MXOUT_AUTH_TOKEN=<TOKEN_SECRETO>

Dev vs produção

Aspecto Dev (local) Prod (Runner/CI-CD)
Acesso http://localhost:8080 HTTPS via Traefik (https://mxout.exemplo.com)
Deploy docker compose up -d manual Push na branch dist aciona o Runner
Config Montada do host Montada via volume declarado no .deploy.yml
Token Valor local de teste Secret gerenciado pelo Runner (--ckey)
Traefik Opcional Obrigatório (termina TLS, roteia domínio)

Em produção, o mxout nunca é exposto diretamente na internet. O Traefik recebe o tráfego HTTPS e encaminha para a porta 8080 interna.

O deploy em produção passa pelo CI/CD Runner (branch dist + .deploy.yml). Não use docker compose up direto no servidor de produção.

Verificar entrega

Após subir o serviço, envie um e-mail de teste e acompanhe os eventos do ledger:

docker logs -f mxout | grep 'mxout::ledger'

Ver também: Ledger — mecanismo de controle.

Referências

By Borlot.com.br on 01/06/2026