The bounce message “mail for example.com loops back to myself” is a routing error, not a delivery timeout. It appears in logs as status=bounced (mail for example.com loops back to myself) with DSN 5.4.6. Postfix resolved the destination domain’s MX record, found the best-preference MX points at this host, but found no matching entry in mydestination, virtual_mailbox_domains, virtual_alias_domains, or relay_domains. Instead of looping, Postfix bounces the message.
This typically surfaces after a DNS or MX change, a relay-host migration, adding a virtual domain without updating delivery maps, or converting a null client to a full internet site. The fix is to align the domain ownership tables with the MX record. Raising retry limits will not help because the routing decision itself is wrong.
The error often hits only some domains while others deliver normally. That narrow scope points to a per-domain configuration gap, not a system-wide failure. Start there.
What this means
Postfix’s smtp(8) delivery agent performs MX resolution for each outbound domain. When the best-preference record resolves to an IP that belongs to the local machine, Postfix checks whether it “owns” that domain. Ownership is determined by four parameters:
mydestination: domains delivered locally to UNIX accounts via thelocaldelivery agent.virtual_mailbox_domains: domains delivered to virtual mailboxes via thevirtualdelivery agent.virtual_alias_domains: domains handled purely by alias rewriting.relay_domains: domains this host accepts and forwards to another destination.
If the MX points at this host but the domain appears in none of these lists, Postfix detects a loop and bounces the message rather than connecting to itself repeatedly.
The error can also fire when Postfix does not recognize its own IP addresses. If the server sits behind NAT or a proxy and the MX record points to a public IP that Postfix does not know about, the loop detection logic fails. The proxy_interfaces parameter exists for this scenario.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Domain missing from mydestination | Mail for a local domain bounces; MX points here | postconf -h mydestination |
| Domain missing from virtual_mailbox_domains | Virtual mailbox domain bounces; SQL query returns nothing | postmap -q domain mysql:/etc/postfix/mysql-virtual_domains.cf |
| Domain missing from relay_domains | Backup MX or gateway loops for a relayed domain | postconf -h relay_domains |
| Server behind NAT without proxy_interfaces | MX resolves to public IP Postfix does not recognize as its own | postconf -h proxy_interfaces |
| relay_domains inherits empty mydestination | Gateway or null-client: mydestination empty, relay_domains also empty because it defaults to $mydestination | postconf -h mydestination relay_domains |
| Inactive or typo’d domain in SQL table | Domain exists in database but query returns no rows (inactive flag, typo) | Run the actual SQL query or postmap -q lookup |
| transport_maps missing route | Domain in relay_domains but no transport entry routes it onward | postconf -h transport_maps and test lookup |
Quick checks
Run these read-only commands to narrow the problem. None modify configuration.
# Show all domain ownership parameters at once
postconf -h mydestination virtual_mailbox_domains virtual_alias_domains relay_domains
# Check NAT/proxy awareness
postconf -h proxy_interfaces inet_interfaces
# Check transport maps and relay transport
postconf -h transport_maps relay_transport
# Check what hostname Postfix identifies as
postconf -h myhostname
# Resolve the MX for the affected domain
dig +short MX example.com
# Find all affected domains in the bounce logs
grep 'loops back to myself' /var/log/mail.log | tail -20
# Test a virtual domain lookup (adjust map type and path to your setup)
postmap -q example.com mysql:/etc/postfix/mysql-virtual_domains.cf
# Verify local and virtual recipient maps are configured
postconf -h local_recipient_maps virtual_mailbox_maps
# Confirm Postfix is listening and the greeting matches myhostname
echo QUIT | nc -w 5 localhost 25 | head -1
If your system logs to /var/log/maillog (RHEL and derivatives) instead of /var/log/mail.log (Debian and Ubuntu), adjust the grep commands accordingly.
How to diagnose it
The diagnostic flow is a sequence of elimination. Work through it methodically.
flowchart TD
A["Bounce: loops back to myself"] --> B{"Domain in mydestination?"}
B -->|No| C{"Domain in virtual_mailbox_domains?"}
B -->|Yes| D["Verify local delivery agent handles it"]
C -->|No| E{"Domain in relay_domains?"}
C -->|Yes| F["Verify virtual delivery agent handles it"]
E -->|No| G{"Does MX point to this host?"}
E -->|Yes| H["Verify transport_maps routes onward"]
G -->|Yes| I["Add domain to correct table"]
G -->|No| J{"Is server behind NAT/proxy?"}
J -->|Yes| K["Set proxy_interfaces to public IP"]
J -->|No| L["Check DNS and MX records"]Identify affected domains. Extract them from the bounce logs. A single domain points to a missing table entry. Multiple domains suggest a broader routing change or a NAT/proxy issue.
Determine which table should own each domain. Is the domain for local UNIX accounts, virtual mailboxes, alias-only rewriting, or relay to another host? A null client or outbound-only relay typically has an empty
mydestination. An internet site with virtual hosting usesvirtual_mailbox_domains. A backup MX usesrelay_domains.Verify the domain is present in that table. Use
postmap -qto test the actual lookup, not justgrepon the source file. For SQL-backed maps, the database might have the domain but with an inactive flag or a typo. The lookup query is what Postfix evaluates.Resolve the MX and confirm it points to this host. Run
dig +short MX example.com. Compare the resolved hostname or IP againstmyhostname,inet_interfaces, andproxy_interfaces. If the MX resolves to a public IP and this server is behind NAT, Postfix may not recognize the address as its own.Check proxy_interfaces if behind NAT. This parameter must list the public IP address(es) that the MX record resolves to. Without it, Postfix sees only its private IP and cannot match it against the MX-resolved address.
Check transport_maps for relayed domains. If the domain is in
relay_domainsbuttransport_mapshas no entry for it, Postfix uses the default relay transport. If that transport points back at this host or is misconfigured, the loop persists.Check the relay_domains default trap. The
relay_domainsparameter defaults to$mydestination. Ifmydestinationis empty (common on gateway or null-client setups),relay_domainsis also empty. Any domain whose MX points to this host will loop. Setrelay_domainsexplicitly.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| Bounce rate (status=bounced) | Routing errors produce bounces, not deferrals | Sudden spike with “loops back to myself” as the reason |
| Delivery rate by domain | Isolates which domains are affected | Delivery to one domain drops to zero while others are normal |
| Deferred queue growth | Looped mail may defer before bouncing | Queue grows for specific domains before bounces appear |
| DNS resolver responsiveness | MX resolution must be correct for routing | Lookups returning unexpected or stale records |
| Postfix configuration state | Config changes cause the mismatch | Changes to mydestination, relay_domains, or virtual maps correlate with onset |
Fixes
Fix the routing decision. Each cause has a specific remedy.
The commands below modify main.cf or map files. Review the current value before applying, and run postfix reload after changes to pick up the new configuration.
Domain belongs in mydestination
If the domain is delivered to local UNIX accounts, add it to mydestination:
# Review current value
postconf -h mydestination
# Add the domain (this replaces the entire value; include existing entries)
postconf -e 'mydestination = $myhostname, localhost.$mydomain, example.com'
# Reload to apply
postfix reload
Do not list the same domain in both mydestination and virtual_mailbox_domains. Postfix logs a warning about overlapping domains and delivery becomes unpredictable.
Domain belongs in virtual_mailbox_domains
If the domain uses virtual mailboxes, ensure it appears in the virtual domains map. For file-backed maps:
# Add to the source file, then rebuild the indexed map
echo "example.com OK" >> /etc/postfix/virtual_domains
postmap /etc/postfix/virtual_domains
# Verify the lookup returns a result
postmap -q example.com hash:/etc/postfix/virtual_domains
For SQL-backed maps, verify the domain is active in the database. Check for an active column or similar flag. A common failure is the domain row existing but marked inactive, so the lookup query returns no rows. Run the exact query from your map file against the database to confirm.
Domain belongs in relay_domains
For a relay or gateway host that forwards mail to another internal server, add the domain to relay_domains and configure transport_maps to route it onward:
# Add the domain (this replaces the entire value; include existing entries)
postconf -e 'relay_domains = example.com'
# Add a transport entry to route onward
echo "example.com smtp:[internal-mail.example.lan]" >> /etc/postfix/transport
postmap /etc/postfix/transport
postfix reload
The square brackets around [internal-mail.example.lan] suppress MX lookup and force direct A-record resolution. Use this for internal relays that should not go through public DNS.
If relay_domains was inheriting an empty mydestination, set it explicitly rather than relying on the default.
Backup MX without relay_domains
A backup MX that queues mail for a primary must list the backed-up domain in relay_domains. Do not list it in mydestination (that would attempt local delivery). The domain belongs in relay_domains only, and Postfix will queue and retry delivery to the primary MX.
NAT or proxy without proxy_interfaces
If the server is behind NAT or a proxy and the MX record points to a public IP, set proxy_interfaces to that public address:
postconf -e 'proxy_interfaces = 203.0.113.10'
postfix reload
This tells Postfix the public IP belongs to this machine, so the loop detection logic recognizes the MX target as itself.
transport_maps missing or wrong
If the domain is in relay_domains but no transport entry exists, Postfix uses the default relay transport. Verify the transport map:
# Check configured transport maps
postconf -h transport_maps
# Test a transport lookup
postmap -q example.com hash:/etc/postfix/transport
Add the entry if missing, or correct the destination if it points back at this host.
Prevention
Audit domain tables after every DNS or MX change. When you add, remove, or change an MX record, verify that the affected domain exists in the correct Postfix parameter. A domain whose MX moves to this host without a corresponding mydestination or relay_domains entry will loop.
Test with postmap -q before announcing the domain. For SQL-backed virtual domains, run the actual lookup query. Do not assume the database row is correct. Check active flags and spelling.
Version-control main.cf and master.cf. Configuration drift causes this error more often than DNS changes. Knowing what changed and when collapses diagnosis from minutes to seconds. See postfix check warnings: configuration drift and permission problems.
Run postfix check after changes. It catches overlapping domain declarations, permission problems, and syntax errors before they cause bounces.
Monitor bounce rate by domain. A routing misconfiguration for one domain produces a spike in bounces for that domain while overall mail flow looks healthy. Alert on per-domain bounce rates, not just aggregate counts. See Postfix monitoring checklist: the signals every production mail server needs.
How Netdata helps
- Per-second bounce detection. Netdata surfaces the bounce rate spike from a routing error within seconds, often before users report missing mail. Correlating the bounce timestamp with configuration changes narrows the cause.
- Delivery rate by domain. Delivery to one domain dropping to zero while others remain healthy confirms a per-domain routing issue rather than a system-wide failure.
- Deferred queue growth tracking. Looped mail may enter the deferred queue before bouncing. Queue depth metrics show the growth pattern that precedes the bounces.
- DNS resolver health. MX resolution is central to this error. Monitoring DNS latency and failure rate independently confirms whether the resolver is contributing.
- Configuration change correlation. When bounce rate spikes, correlating the timestamp against recent system-level changes helps identify the triggering edit.
Related guides
- Postfix deferred queue growing: why mail piles up and how to drain it
- Postfix flushing and clearing the deferred queue: postqueue and postsuper
- postfix check warnings: configuration drift and permission problems
- How Postfix actually works in production: a mental model for operators
- Postfix mail flow: injection rate outpacing delivery rate
- Postfix monitoring checklist: the signals every production mail server needs






