# Email Server Diagnostics

> Diagnose and analyze self-hosted email server configurations. Checks DNS records (MX, SPF, DKIM, DMARC, MTA-STS, TLSRPT, rDNS, autodiscover, BIMI, DANE/TLSA), TLS certificates (HTTPS, IMAPS, SMTPS, protocol versions), port connectivity, IP/domain blacklist status, SPF lookup limits, open relay, IPv6 consistency, and server internals (Postfix, Dovecot, mailboxes, DKIM keys, mail queue). Also validates against Google/Yahoo/Microsoft 2024-2025 bulk sender requirements. Use this skill whenever the user mentions email server health checks, mail deliverability issues, DNS configuration for email, email authentication setup (SPF/DKIM/DMARC), mail server migration, diagnosing why emails go to spam or get rejected, or checking compliance with Gmail/Outlook sender policies. Also trigger when the user asks to set up or verify a mail domain, check email security, audit an existing mail server (mailcow, iRedMail, Postfix, etc.), or investigate email bounces.

- Skill: `arova-ai/email-server-diagnostics` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add arova-ai/email-server-diagnostics`
- Raw SKILL.md: https://api.skillmd.com/api/skills/arova-ai/email-server-diagnostics/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: arova-ai (https://skillmd.com/u/arova-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/arova-ai/email-server-diagnostics

---


# Email Server Diagnostics

Perform a comprehensive diagnostic analysis of a self-hosted email service. Identify misconfigurations, missing records, and security issues that affect deliverability and security, then produce a clear report with prioritized recommendations.

## Information Gathering

Before running checks, establish:

1. **Mail domain(s)** — the part after @ in email addresses (e.g., `example.com`)
2. **Mail hostname** — server FQDN, usually the MX target (e.g., `mail.example.com`)
3. **Server public IP** — the IP external mail servers connect to
4. **Server access** — SSH credentials or other access (for internal checks)
5. **Mail server software** — mailcow, iRedMail, Postfix/Dovecot standalone, etc.

If not all provided, infer from DNS: MX → hostname → A → IP. Ask for the rest.

## Diagnostic Sequence

Run external checks (1-5) first — they need no server access. Internal checks (6) require SSH.

### 1. DNS Records

For each mail domain, check every record below using `dig`.

| Record | Lookup | What to verify |
|---|---|---|
| **MX** | `dig MX <domain> +short` | Points to correct hostname; priority reasonable; no stale entries |
| **A / AAAA** | `dig A <hostname>` + `dig AAAA <hostname>` | Resolves to expected IP; if AAAA exists, IPv6 must be fully configured |
| **SPF** | `dig TXT <domain> +short` | Has `v=spf1`; includes server IP or MX; ends `-all` or `~all`; exactly ONE SPF record (multiple = error) |
| **DKIM** | `dig TXT <selector>._domainkey.<domain>` | Valid public key; try selectors: `dkim`, `default`, `mail`, `s1`, `selector1`, `selector2`, `k1` |
| **DMARC** | `dig TXT _dmarc.<domain>` | `v=DMARC1`; policy `quarantine` or `reject` for production; has `rua=` for reporting |
| **rDNS (PTR)** | `dig -x <ip> +short` | Matches mail hostname exactly |
| **Forward-confirmed rDNS** | PTR → A lookup | PTR hostname must resolve back to the original IP |
| **MTA-STS** | `dig TXT _mta-sts.<domain>` + `curl https://mta-sts.<domain>/.well-known/mta-sts.txt` | TXT has `v=STSv1; id=...`; policy file accessible over HTTPS |
| **TLSRPT** | `dig TXT _smtp._tls.<domain>` | Has `v=TLSRPTv1; rua=mailto:...` |
| **DANE/TLSA** | `dig TLSA _25._tcp.<hostname>` | If DNSSEC enabled, TLSA record should exist (usage 3, selector 1, type 1 recommended) |
| **BIMI** | `dig TXT default._bimi.<domain>` | Optional; requires DMARC p=reject/quarantine with pct=100 |
| **Autodiscover** | `dig A autodiscover.<domain>` + `dig A autoconfig.<domain>` | Records exist for mail client auto-configuration |
| **SRV** | `dig SRV _imaps._tcp.<domain>`, `_submission._tcp.<domain>`, etc. | Service records for IMAP, SMTP, CalDAV, CardDAV |

Also check for stale records: old provider DKIM CNAMEs (e.g., `gm1._domainkey` → gandimail.net), conflicting SPF records, orphaned MX entries.

#### SPF Deep Validation

SPF has strict limits that are almost never checked manually but cause silent failures:

1. **DNS lookup count (max 10)**: Count all `include`, `a`, `mx`, `redirect`, `exists` mechanisms recursively. Exceeding 10 = PermError = SPF fails.
   ```bash
   # To count, recursively resolve each include and count mechanisms
   dig TXT <domain> +short  # start here, then follow each include
   ```

2. **Void lookup limit (max 2)**: DNS queries that return NXDOMAIN or empty count as void lookups. More than 2 = PermError. Often caused by stale `include:` references to decommissioned services.

3. **Common SPF mistakes to flag**:
   - `+all` or `?all` (too permissive)
   - Deprecated `ptr` mechanism
   - Multiple SPF TXT records on same domain
   - `a` and `mx` mechanisms when they add no value (wastes DNS lookups)

#### DKIM Validation

- **Key length**: Extract from DNS TXT and check. 1024-bit = WARNING (should rotate to 2048-bit). 2048-bit = OK.
- **Key rotation**: Flag if `t=y` (testing mode) is still set in production.
- **Selector naming**: Date-based selectors (e.g., `jan2025`) indicate good rotation practice.
- **2048-bit chunking**: Keys > 255 chars must be split into multiple quoted strings in DNS TXT; verify the record parses correctly.

### 2. TLS / SSL Certificates

Check certs on all mail protocols. For each, verify: not self-signed, not expired, correct CN/SAN, chain complete.

```bash
# HTTPS (web UI)
echo | openssl s_client -connect <ip>:443 -servername <hostname> 2>/dev/null | \
  openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# IMAPS
echo | openssl s_client -connect <ip>:993 -servername <hostname> 2>/dev/null | \
  openssl x509 -noout -subject -issuer -dates

# SMTPS
echo | openssl s_client -connect <ip>:465 -servername <hostname> 2>/dev/null | \
  openssl x509 -noout -subject -issuer -dates

# SMTP STARTTLS (port 25)
echo | openssl s_client -connect <ip>:25 -starttls smtp -servername <hostname> 2>/dev/null | \
  openssl x509 -noout -subject -issuer -dates

# Submission STARTTLS (port 587)
echo | openssl s_client -connect <ip>:587 -starttls smtp -servername <hostname> 2>/dev/null | \
  openssl x509 -noout -subject -issuer -dates
```

#### TLS Protocol Version Check

Flag deprecated protocols. Minimum acceptable is TLS 1.2.

```bash
# Check if TLS 1.0/1.1 still accepted (should NOT be)
openssl s_client -connect <ip>:25 -starttls smtp -tls1 2>&1 | grep "Protocol"
openssl s_client -connect <ip>:25 -starttls smtp -tls1_1 2>&1 | grep "Protocol"
```

Flag: self-signed certs, expired certs, wrong CN, mismatched certs across protocols, TLS < 1.2 still enabled.

### 3. Port Connectivity & SMTP Banner

Test all standard ports:

| Port | Protocol | Purpose |
|---|---|---|
| 25 | SMTP | Incoming mail |
| 80 | HTTP | Web UI, ACME challenges |
| 110 | POP3 | Mail retrieval (legacy) |
| 143 | IMAP | Mail retrieval |
| 443 | HTTPS | Web UI, autodiscover |
| 465 | SMTPS | Submission (implicit TLS) |
| 587 | Submission | Submission (STARTTLS) |
| 993 | IMAPS | Secure IMAP |
| 995 | POP3S | Secure POP3 |
| 4190 | Sieve | Mail filtering |

```bash
nc -z -w5 <ip> <port> && echo "open" || echo "closed"
```

**SMTP banner check** (port 25):
```bash
echo "EHLO test" | nc -w5 <ip> 25
```
Verify: correct hostname in banner, STARTTLS advertised, 250 response codes.

**HELO hostname consistency**: The SMTP banner hostname should match the PTR record, which should match the A record. This three-way match (forward-confirmed reverse DNS) is critical for deliverability.

### 4. IP & Domain Reputation (Blacklists)

Check the server IP against major DNSBLs. A listing on any of these significantly impacts deliverability.

```bash
# Reverse the IP octets for DNSBL query
# For IP 1.2.3.4, query 4.3.2.1.<dnsbl-zone>
# NXDOMAIN = clean; any A record (127.0.0.x) = listed

IP="<server-ip>"
REV=$(echo $IP | awk -F. '{print $4"."$3"."$2"."$1}')

for bl in zen.spamhaus.org b.barracudacentral.org bl.spamcop.net dnsbl.sorbs.net; do
  result=$(dig +short ${REV}.${bl} 2>/dev/null)
  if [ -z "$result" ]; then echo "$bl: CLEAN"
  else echo "$bl: LISTED ($result)"; fi
done

# Domain blacklists
for bl in dbl.spamhaus.org multi.surbl.org multi.uribl.com; do
  result=$(dig +short <domain>.${bl} 2>/dev/null)
  if [ -z "$result" ]; then echo "$bl: CLEAN"
  else echo "$bl: LISTED ($result)"; fi
done
```

Also check IPv6 if AAAA records exist (IPv6 has separate reputation from IPv4).

### 5. Open Relay Test

Verify the server rejects relay attempts from unauthenticated sources. An open relay will get blacklisted very quickly.

```bash
# Connect from an external host without authentication and try to relay
# This should be REJECTED
(echo "EHLO test"; sleep 1; echo "MAIL FROM:<test@external.com>"; sleep 1; echo "RCPT TO:<test@gmail.com>"; sleep 1; echo "QUIT") | nc -w10 <ip> 25
```

Expected: `554` or `550` reject on the RCPT TO command (relay denied). If you get `250 OK` on RCPT TO for an external recipient, the server is an open relay — this is CRITICAL.

### 6. Server Internal Checks (requires SSH)

Adapt to the specific mail software. Database credentials can usually be found in the main config file.

#### Mailcow

```bash
# Key config values from mailcow.conf
grep -E "^(MAILCOW_HOSTNAME|SKIP_LETS_ENCRYPT|ENABLE_IPV6|ACL_ANYONE|HTTP_REDIRECT)" \
  /opt/mailcow-dockerized/mailcow.conf

# Extract DB credentials
DBUSER=$(grep ^DBUSER /opt/mailcow-dockerized/mailcow.conf | cut -d= -f2)
DBPASS=$(grep ^DBPASS /opt/mailcow-dockerized/mailcow.conf | cut -d= -f2)
REDIS_PASS=$(grep ^REDISPASS /opt/mailcow-dockerized/mailcow.conf | cut -d= -f2)

# Domains
cd /opt/mailcow-dockerized
docker compose exec -T mysql-mailcow mysql -u$DBUSER -p$DBPASS mailcow \
  -e "SELECT domain,active,backupmx FROM domain;"

# Mailboxes
docker compose exec -T mysql-mailcow mysql -u$DBUSER -p$DBPASS mailcow \
  -e "SELECT username,domain,active,quota FROM mailbox;"

# Aliases
docker compose exec -T mysql-mailcow mysql -u$DBUSER -p$DBPASS mailcow \
  -e "SELECT address,goto,active FROM alias WHERE address NOT LIKE '@%';"

# DKIM keys (stored in Redis)
docker compose exec -T redis-mailcow redis-cli -a "$REDIS_PASS" HGETALL DKIM_SELECTORS
docker compose exec -T redis-mailcow redis-cli -a "$REDIS_PASS" HKEYS DKIM_PRIV_KEYS

# ACME / Let's Encrypt status
docker compose logs acme-mailcow --tail 30

# Container health
docker compose ps --format "table {{.Name}}\t{{.Status}}"

# Postfix config highlights
docker compose exec -T postfix-mailcow postconf -n | \
  grep -E "(myhostname|relayhost|smtpd_tls|smtpd_relay_restrictions)"

# Mail queue depth
docker compose exec -T postfix-mailcow postqueue -p | tail -1

# Rspamd stats
docker compose exec -T rspamd-mailcow rspamc stat 2>/dev/null | head -20
```

#### Generic Postfix/Dovecot

```bash
postconf -n | grep -E "(myhostname|mydestination|relay|smtpd_tls|smtp_tls|smtpd_relay)"
doveconf -n | grep -E "(ssl|auth_mechanisms|mail_location)"
postqueue -p | tail -1  # queue depth
```

#### Internal checks to verify:
- All containers/services running and healthy
- DKIM private keys exist and match DNS public key selector
- Let's Encrypt working (not self-signed fallback)
- Postfix `myhostname` matches DNS and rDNS
- No open relay (`smtpd_relay_restrictions` contains `reject_unauth_destination`)
- Mail queue not backed up (>100 = investigate, >1000 = problem)
- Rspamd running and processing (not bypassed)

## Google/Yahoo/Microsoft 2024-2025 Compliance Check

These requirements are now enforced with permanent rejections. Check compliance for domains sending to Gmail, Yahoo, or Outlook.com.

**All senders must have:**
- [ ] SPF or DKIM set up (at minimum)
- [ ] Valid PTR (forward-confirmed reverse DNS)
- [ ] TLS for mail transmission
- [ ] RFC 5322 compliant message formatting
- [ ] Spam rate < 0.3% (check via Google Postmaster Tools)

**Bulk senders (>5,000 emails/day) must additionally have:**
- [ ] Both SPF AND DKIM (not just one)
- [ ] DMARC with at least `p=none` and alignment pass
- [ ] One-click unsubscribe headers (List-Unsubscribe + List-Unsubscribe-Post)
- [ ] ARC headers when forwarding messages
- [ ] From: domain aligned with SPF or DKIM domain

**Microsoft Outlook (May 2025+):**
- [ ] SPF, DKIM, and DMARC all required for >5,000 emails/day to Outlook/Hotmail/Live

Flag any non-compliance as WARNING or CRITICAL depending on sending volume.

## IPv6 Consistency Check

Only if the mail hostname has an AAAA record. Having a partially configured IPv6 is worse than having none.

```bash
AAAA=$(dig AAAA <hostname> +short)
if [ -n "$AAAA" ]; then
  echo "IPv6: $AAAA"
  # Must have IPv6 PTR
  dig -x $AAAA +short  # should return the hostname
  # Must be in SPF (ip6: mechanism)
  dig TXT <domain> +short | grep -o "ip6:[^ ]*"
  # Check IPv6 blacklists separately
fi
```

If AAAA exists but PTR is missing or SPF doesn't include it → CRITICAL (Gmail rejects immediately).

## Diagnostic Report Format

```markdown
## <domain> Email Server Diagnostic Report

### Server Overview
| Item | Value |
|---|---|
| Mail hostname | ... |
| Public IP | ... |
| rDNS (PTR) | ... |
| Server software | ... |
| Services status | X/Y running |

### Mailboxes & Aliases
(table of accounts and forwarding rules)

### DNS Records
| Record | Value | Status |
|---|---|---|
(OK / WARNING / MISSING / ERROR for each)

### SPF Analysis
- Record: `v=spf1 ...`
- DNS lookups used: X/10
- Void lookups: X/2
- Issues: (if any)

### DKIM Analysis
- Selector: ...
- Key length: ... bits
- Status: ...

### TLS Certificates
| Protocol | Port | Issuer | Expires | TLS Version | Status |
|---|---|---|---|---|---|

### Port Connectivity
| Port | Protocol | Status |
|---|---|---|

### Blacklist Status
| Blacklist | Status |
|---|---|
(CLEAN or LISTED for each)

### Compliance: Google/Yahoo/Microsoft Sender Requirements
(checklist with pass/fail for each requirement)

### Issues Found
1. **CRITICAL** — blocks delivery or causes rejection
2. **WARNING** — degrades deliverability or security
3. **INFO** — best-practice recommendations

### Recommended Actions
(numbered list, priority order, with specific commands or steps)
```

## Severity Guidelines

**CRITICAL** (mail delivery blocked):
- MX record missing or wrong
- SPF/DKIM/DMARC completely absent
- SPF exceeds 10 DNS lookups
- TLS certificate expired or invalid on mail ports
- Port 25 unreachable
- Open relay detected
- IP listed on Spamhaus ZEN
- IPv6 AAAA exists but no IPv6 PTR
- HELO hostname doesn't match rDNS

**WARNING** (deliverability degraded):
- rDNS doesn't match mail hostname
- SPF uses `~all` with DMARC `p=reject` (inconsistent)
- DMARC policy still at `p=none` in production
- DKIM using 1024-bit key
- Self-signed certificates on any port
- MTA-STS missing
- Stale DNS records from old providers
- ACME/Let's Encrypt failing
- Mail queue > 100 messages
- TLS 1.0 or 1.1 still accepted
- Non-compliant with bulk sender requirements

**INFO** (best practices):
- TLSRPT not configured
- BIMI not set up (requires DMARC p=reject + pct=100)
- DANE/TLSA not configured (requires DNSSEC)
- ARC not configured for forwarding
- SRV records for autodiscover missing
- POP3 ports open but unused
- DKIM key rotation not evident (no date-based selectors)
- Void lookup count close to limit

