Introduction
MX (Mail Exchange) records direct email to the correct mail servers for your domain. When these records are incorrect, missing, or misconfigured, email bounces, gets rejected, or routes to the wrong servers. MX record problems are particularly critical because they directly impact business communication, and misconfigurations can remain unnoticed until someone reports missing email.
Symptoms
- Incoming emails bounce with "domain not found" or "no MX records"
- Email routes to old mail server after migration
- Some senders can reach you while others cannot
- Email delayed or delivered to wrong addresses
- NDR (Non-Delivery Report) messages mention DNS or MX lookup failures
- Mail server logs show "no such domain" for your domain
- SPF/DKIM/DMARC failures due to MX-related configuration issues
Common Causes
- MX records pointing to wrong hostname or IP
- Mail server hostname has no A or AAAA record
- MX priority values misconfigured (lower priority server preferred first)
- MX records missing entirely
- CNAME used where A record needed for mail server hostname
- TTL too high causing slow propagation after changes
- Mail server hostname in different domain without proper resolution
Step-by-Step Fix
- 1.Query current MX records to see what's configured.
```bash # Get MX records using dig dig example.com MX +short
# Output format: priority hostname # 10 mail.example.com. # 20 mail2.example.com.
# Use host command for simpler output host -t MX example.com
# nslookup alternative nslookup -type=MX example.com
# Query authoritative nameserver directly dig @ns1.yourprovider.com example.com MX ```
- 1.Verify the MX hostname resolves to a valid IP address.
```bash # Get the mail server hostname from MX record mx_host=$(dig example.com MX +short | head -1 | awk '{print $2}') echo "MX hostname: $mx_host"
# Check if it has an A record dig ${mx_host%.} A +short
# Check for AAAA record (IPv6) dig ${mx_host%.} AAAA +short
# Both should return valid IP addresses # If empty, the mail server hostname doesn't resolve
# Common issue: MX points to mail.example.com # but no A record exists for mail.example.com ```
- 1.Check MX record priorities are correct.
```bash # MX priority: lower number = higher preference # Primary server should have lowest number
# Example correct setup: # 10 mail.example.com. (primary - tried first) # 20 mail2.example.com. (secondary - tried if primary fails)
# Check priorities dig example.com MX +short | sort -n
# Common mistakes: # - Same priority for all servers (random distribution, not failover) # - Backup server has lower priority than primary # - Priority 0 used incorrectly (valid but unusual) ```
- 1.Test the mail server hostname connectivity.
```bash # Test SMTP port (25) connectivity to mail server mx_host=$(dig example.com MX +short | head -1 | awk '{print $2}') mx_ip=$(dig ${mx_host%.} A +short | head -1)
echo "Testing SMTP on $mx_host ($mx_ip):" nc -vz $mx_ip 25
# Or use telnet telnet $mx_ip 25
# Test SMTP greeting echo "QUIT" | nc $mx_ip 25 | head -5
# Should see: # 220 mail.example.com ESMTP Postfix # Or similar SMTP banner ```
- 1.Verify no CNAME conflict with MX hostname.
```bash # Mail server hostname should have A record, NOT CNAME mx_host=$(dig example.com MX +short | head -1 | awk '{print $2}')
# Check for CNAME dig ${mx_host%.} CNAME +short
# If this returns a value, you have a CNAME # This can cause issues with some mail servers
# Correct setup: # mail.example.com IN A 192.0.2.10
# Problematic setup: # mail.example.com IN CNAME mail.provider.com. # mail.provider.com IN A 192.0.2.10
# CNAME for MX hostname works for delivery but can break: # - Some anti-spam checks # - Mail header construction # - DKIM signing ```
- 1.Compare with provider's recommended MX records.
```bash # Google Workspace MX records: # 10 smtp.google.com. # 20 smtp2.google.com.
# Microsoft 365 MX records: # 10 example-com.mail.protection.outlook.com.
# Check your provider's documentation for correct values
# Verify your records match recommendations echo "Your MX records:" dig example.com MX +short
# Common provider issues: # - Using old provider MX after migration # - Missing required records for provider # - Wrong hostname format ```
- 1.Check for missing MX record (using A record fallback).
```bash # If no MX record exists, mailers try A record of domain # This is fallback behavior, not recommended
# Check if domain has A record dig example.com A +short
# If MX is missing, some mail will try the A record IP # But this is unreliable and should not be relied on
# Proper setup: Always have explicit MX records dig example.com MX +short # Should return records, not empty ```
- 1.Verify reverse DNS (PTR) for mail server IP.
```bash # Get IP of mail server mx_ip=$(dig mail.example.com A +short | head -1)
# Check reverse DNS dig -x $mx_ip +short
# Should return the mail server hostname # mail.example.com.
# If reverse DNS doesn't match, some mail servers will reject your mail # Fix by contacting your IP provider to set PTR record
# Common requirement: HELO name should match PTR # Mail server config: helo = mail.example.com # PTR record: $mx_ip -> mail.example.com ```
- 1.Test email delivery with real SMTP transaction.
```bash # Use swaks or telnet for SMTP test # Using telnet: telnet mail.example.com 25
# SMTP session: HELO test.example.com MAIL FROM:<test@example.com> RCPT TO:<user@example.com> DATA Subject: Test Message
This is a test. . QUIT
# Watch for responses: # 250 OK - address accepted # 550 - address rejected # 451 - temporary failure
# Or use swaks for easier testing: swaks --to user@example.com --server mail.example.com ```
- 1.Fix MX records with correct configuration.
```bash # Correct MX record format in BIND zone: example.com. 3600 IN MX 10 mail.example.com. example.com. 3600 IN MX 20 mail2.example.com.
# Ensure mail server has A record: mail.example.com. 3600 IN A 192.0.2.10 mail2.example.com. 3600 IN A 192.0.2.20
# For external mail providers (Google Workspace): example.com. 3600 IN MX 1 smtp.google.com. example.com. 3600 IN MX 5 smtp2.google.com. example.com. 3600 IN MX 10 smtp3.google.com. example.com. 3600 IN MX 15 smtp4.google.com.
# After changes, reload zone rndc reload example.com ```
Verification
Complete MX verification checklist:
```bash # 1. Verify MX records exist echo "=== MX Records ===" dig example.com MX +short
# 2. Verify mail server hostnames resolve echo "=== Mail Server IPs ===" for mx in $(dig example.com MX +short | awk '{print $2}'); do echo -n "$mx: " dig ${mx%.} A +short | head -1 done
# 3. Verify priorities are correct echo "=== Priority Order ===" dig example.com MX +short | sort -n
# 4. Test SMTP connectivity echo "=== SMTP Test ===" mx_ip=$(dig $(dig example.com MX +short | head -1 | awk '{print $2}') A +short | head -1) nc -vz $mx_ip 25 && echo "SMTP port 25: OPEN"
# 5. Verify reverse DNS echo "=== Reverse DNS ===" dig -x $mx_ip +short
# 6. Check SPF record echo "=== SPF Record ===" dig example.com TXT +short | grep "v=spf1"
# 7. Check DKIM echo "=== DKIM ===" dig default._domainkey.example.com TXT +short ```
Common MX Configuration Patterns
```bash # Self-hosted mail server: example.com. IN MX 10 mail.example.com. mail.example.com. IN A 192.0.2.10
# Google Workspace: example.com. IN MX 1 smtp.google.com.
# Microsoft 365: example.com. IN MX 10 example-com.mail.protection.outlook.com.
# Dual provider (migration): example.com. IN MX 10 mail.provider1.com. example.com. IN MX 100 mail.provider2.com.
# Backup MX: example.com. IN MX 10 mail.example.com. example.com. IN MX 50 backup.example.net. ```
Monitor MX records regularly and test email delivery paths to catch issues before users report them.