- infra/ansible/roles/mail: refactor OpenDKIM to support multiple signing domains via opendkim_signing_domains list (root + send.performancewest.net). Loops keygen/ownership/keytable/signingtable so the live two-domain setup is reproducible from ansible. - infra/ansible group_vars: add bulk_mail_subdomain + campaign_from_* + campaign_reply_to documentation vars (map to CAMPAIGN_FROM / HC_CAMPAIGN_FROM env read by the builder scripts). smtp_from (transactional) stays on root. - docs/deliverability.md: rewrite TL;DR with the carrierone-vs-performancewest A/B proof (same server/IPs, different From domain -> Inbox vs Junk) and the ~85% Microsoft / 14% Google / <1% Yahoo audience mix; add the bulk-subdomain section, SPF trim, rehab-disabled, and the Hestia DNS automation runbook.
35 lines
1.7 KiB
YAML
35 lines
1.7 KiB
YAML
---
|
|
# OpenDKIM signing for outbound mail (Postfix milter).
|
|
#
|
|
# CRITICAL: campaign mail is injected into Postfix from the Listmonk containers
|
|
# over the Docker bridge network, NOT from localhost. OpenDKIM only signs mail
|
|
# whose client is in InternalHosts; if the Docker subnet is missing there,
|
|
# OpenDKIM *verifies* (rather than *signs*) campaign mail, so every cold email
|
|
# goes out UNSIGNED. Since Feb 2024 Gmail/Yahoo require DKIM on bulk mail, so
|
|
# unsigned campaigns get junked/blocked (this caused the Jun 2026 deliverability
|
|
# collapse: ~23% delivery, Gmail 550-5.7.1). The Docker subnet below MUST be in
|
|
# opendkim_internal_hosts.
|
|
opendkim_selector: mail
|
|
opendkim_signing_domain: performancewest.net
|
|
opendkim_socket: "inet:8891@localhost"
|
|
|
|
# Signing domains. The root domain carries transactional/verification mail; the
|
|
# dedicated bulk subdomain (send.performancewest.net) carries Listmonk campaign
|
|
# mail so its sending reputation is isolated from the root domain (which then
|
|
# stays clean and recovers faster). Each entry generates its own key + selector
|
|
# and contributes a line to KeyTable/SigningTable. The first entry is treated as
|
|
# the primary (kept for backwards-compat with opendkim_signing_domain above).
|
|
# See docs/deliverability.md.
|
|
opendkim_signing_domains:
|
|
- domain: "{{ opendkim_signing_domain }}"
|
|
selector: "{{ opendkim_selector }}"
|
|
- domain: "send.performancewest.net"
|
|
selector: "send"
|
|
|
|
# Hosts OpenDKIM will SIGN for (vs verify). Must include the Docker bridge
|
|
# subnet so Listmonk container traffic is signed.
|
|
opendkim_internal_hosts:
|
|
- "127.0.0.1"
|
|
- "localhost"
|
|
- "172.16.0.0/12" # Docker bridge networks (Listmonk, workers, etc.)
|
|
- "10.0.0.0/8"
|