Sharing MailCue across projects

Run one MailCue container as a shared development dependency that multiple consumer projects connect to over a Docker network.

Sharing mailcue across projects

MailCue is designed as a shared development dependency. Multiple consumer projects (fase, and other Olib products) can talk to a single running MailCue container at the same time without embedding it in their own compose files. Coupling happens at the Docker-network layer, so mailcue stays standalone.

One-time host setup

# Create the shared external network (once per host, never again)
docker network create mailcue-net

Bring up mailcue

cd /path/to/mailcue
docker compose up -d

MailCue attaches to mailcue-net with a static IPv4, by default 172.28.0.10. Override via the MAILCUE_SANDBOX_IP env var if that address collides with another network on your host:

MAILCUE_SANDBOX_IP=172.28.0.20 docker compose up -d

Consumer-project integration

Inside a consumer project's own docker-compose.yml (or its dev-only docker-compose.override.yml) declare the network as external, attach the services that need MailCue, and map every hostname you want to intercept to MailCue's IP via extra_hosts:

services:
  backend:
    # ... your service definition ...
    networks:
      - your-project-net    # your project's own bridge
      - mailcue-net         # shared mailcue bridge
    extra_hosts:
      # Email - resolve mailcue's FQDN to its IP
      - "mail.mailcue.local:172.28.0.10"
      # Outbound HTTP(S) interception - point every upstream hostname you
      # want the sandbox to capture at mailcue's Nginx, which serves
      # CA-signed leaf certs for each:
      - "api.example-provider-a.com:172.28.0.10"
      - "api.example-provider-b.com:172.28.0.10"
      # ... etc.

networks:
  mailcue-net:
    external: true

The consumer project must also trust mailcue's Root CA so outbound SDKs complete their TLS handshake. Copy /var/lib/mailcue/certs/provider_ca.crt out of the running container (it is identical to the email CA at /etc/ssl/mailcue/ca.crt) and install it into the consumer's image with update-ca-certificates at build time:

docker compose exec mailcue \
    cat /var/lib/mailcue/certs/provider_ca.crt \
    > /path/to/consumer/certs/mailcue-ca.crt

Why an external network (not build: ../mailcue in your compose)

Baking build: ../mailcue or a mailcue: service block into a consumer project's compose couples mailcue's lifecycle (healthchecks, volumes, depends_on) to that consumer. Any other project that needs the same sandbox either (a) duplicates the coupling, producing two mailcue containers that can't share state, or (b) breaks when the consumer stops. An external network inverts that: mailcue owns its own compose, its own volumes, and its own healthcheck; every consumer just plugs into the bus.

Production

Production deployments do not use mailcue-net. Real upstream DNS resolves to real upstreams; real TLS is validated against real CAs. See the Production deployment guide for the docker-compose.deploy.yml workflow.