A sender gets 550 5.1.1 <user@domain>: Recipient address rejected: User unknown in local recipient table (or the virtual mailbox or relay variant). Postfix rejected the recipient at SMTP RCPT TO time, before queueing, because the address was not found in the map for that address class. This validation is controlled by smtpd_reject_unlisted_recipient (default: yes).

The problem is either that a legitimate recipient is missing from the map, or that the domain is in the wrong address class and Postfix is checking the wrong map. Before changing restrictions or disabling validation, identify which map Postfix consulted, test the lookup with postmap -q, and reconcile the map against your authoritative directory. The reject string identifies the address class.

What this means

Each address class has its own recipient validation map. The three reject strings and their corresponding maps:

  • “User unknown in local recipient table” - Domain is in mydestination. Postfix checks local_recipient_maps (default: proxy:unix:passwd.byname $alias_maps).
  • “User unknown in virtual mailbox table” - Domain is in virtual_mailbox_domains. Postfix checks virtual_mailbox_maps.
  • “User unknown in relay recipient table” - Domain is in relay_domains. Postfix checks relay_recipient_maps.

If a map parameter is empty, Postfix accepts all recipients for that class without checking. This is dangerous on internet-facing systems because it produces backscatter.

The key diagnostic question: which address class does the rejected domain belong to, and does the corresponding map contain the recipient?

flowchart TD
    A["Recipient user@domain rejected"] --> B["Which reject string?"]
    B -->|"local recipient table"| C["Domain in mydestination"]
    B -->|"virtual mailbox table"| D["Domain in virtual_mailbox_domains"]
    B -->|"relay recipient table"| E["Domain in relay_domains"]
    C --> F["Map: local_recipient_maps"]
    D --> G["Map: virtual_mailbox_maps"]
    E --> H["Map: relay_recipient_maps"]
    F --> I["Test: postmap -q user@domain map"]
    G --> I
    H --> I

Common causes

CauseWhat it looks likeFirst thing to check
Domain in wrong address classA domain that should be relayed or hosted as virtual mailbox is listed in mydestination, so Postfix checks local_recipient_maps instead of the correct mappostconf -h mydestination virtual_mailbox_domains relay_domains
Stale or incomplete recipient mapUser exists in the directory but not in the Postfix map; .db file not rebuilt after the directory changedpostmap -q user@domain against the map, then check .db mtime
Map backend unresponsive or slowValid users intermittently rejected; smtpd processes accumulate; delivery latency risesTime a postmap -q query against network-backed maps (LDAP, SQL)
Dictionary attackSpike of “User unknown” rejections from few source IPs; low legitimate message volumeCount rejects by client IP in mail logs
Map source out of syncRecent provisioning changes not propagated to the map file or query resultCompare map contents against directory export

Quick checks

# Show which recipient maps are configured
postconf -h local_recipient_maps virtual_mailbox_maps relay_recipient_maps

# Show domain-to-address-class assignments
postconf -h mydestination virtual_mailbox_domains relay_domains

# Confirm smtpd_reject_unlisted_recipient is enabled
postconf -h smtpd_reject_unlisted_recipient

# Find recent rejections (path varies: mail.log on Debian, maillog on RHEL)
grep 'User unknown' /var/log/mail.log | tail -20
# or: grep 'User unknown' /var/log/maillog | tail -20

# Count rejections by source IP in the last hour
grep "$(date '+%b %e %H' -d '1 hour ago')" /var/log/mail.log \
  | grep 'User unknown' \
  | grep -oE 'client=[^]]+\[[0-9.]+' \
  | sort | uniq -c | sort -rn | head -10

# Test a specific map lookup directly
postmap -q testuser@example.com hash:/etc/postfix/relay_recipients

# Check map file freshness (.db mtime for hash/btree maps)
stat /etc/postfix/relay_recipients.db

# Time a network-backed map query to check backend responsiveness
time postmap -q testuser@example.com mysql:/etc/postfix/mysql-virtual.cf

How to diagnose it

  1. Extract the rejected address, domain, and reject string from the logs. The reject line includes to=<user@domain> and the specific reject string (“local recipient table”, “virtual mailbox table”, or “relay recipient table”). The string identifies the address class.

  2. Verify the domain’s address class. Check whether the domain appears in mydestination, virtual_mailbox_domains, or relay_domains:

    postconf -h mydestination virtual_mailbox_domains relay_domains
    

    A domain in the wrong class is the most common cause. A relay domain accidentally left in mydestination causes Postfix to check local_recipient_maps (UNIX passwd and aliases) instead of relay_recipient_maps, rejecting every valid relay recipient.

  3. Test the map lookup directly. Identify the map from postconf, then query it with postmap -q using the exact recipient address:

    postconf -h relay_recipient_maps
    postmap -q user@domain hash:/etc/postfix/relay_recipients
    

    If postmap -q returns nothing, the map does not contain the recipient. If it returns a value but Postfix still rejects, verify you tested the same map type:path from postconf output (including any proxy: prefix) and that Postfix has reloaded after recent changes.

  4. Verify the user exists in the authoritative source. Check the directory, database, or passwd file directly. If the user does not exist there, the rejection is legitimate.

  5. Check map freshness. For hash or btree maps, compare the .db file mtime against the last provisioning change:

    stat /etc/postfix/relay_recipients.db
    

    If the .db predates recent changes, rebuild it.

  6. Rule out backend slowness for network-backed maps. Time the query:

    time postmap -q user@domain ldap:/etc/postfix/ldap-recipients.cf
    

    Query times above 500ms indicate backend latency that can cause intermittent rejections or smtpd starvation under load.

  7. Determine if this is a spike or a persistent issue. A sudden increase in “User unknown” rejections from few source IPs, with low overall legitimate volume, is a dictionary attack, not a configuration problem. Count rejects by client IP to distinguish.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Reject count by categoryDistinguishes dictionary attacks from configuration driftSudden spike in RCPT rejects, especially from a single source IP
Bounce rateSustained rejections of valid recipients generate bounces to sendersBounce rate above 1% sustained
Map query latencySlow network-backed maps cause intermittent rejections and smtpd accumulationQuery time above 500ms for LDAP or SQL maps
Map freshness (.db mtime)Stale maps reject valid users added after last rebuild.db mtime predates last directory provisioning change
smtpd process utilizationSlow map queries hold smtpd processes openProcess count approaching maxproc with low throughput
Per-client connection rateHigh rate from single IP indicates address harvestingMore than 100 connections/minute from one source

Fixes

Domain classified into the wrong address class

The most common cause. A domain that should be relayed or hosted as a virtual mailbox is in mydestination.

Remove the domain from mydestination and add it to the correct class parameter. Then ensure the corresponding map contains valid recipients for that domain.

# Review current assignments before making changes
postconf -h mydestination relay_domains virtual_mailbox_domains

# WARNING: postconf -e replaces the entire parameter value.
# Review your current settings and adjust accordingly.
postconf -e 'mydestination = $myhostname, localhost.$mydomain, localhost'
postconf -e 'relay_domains = example.com, other.example.com'

# Ensure relay_recipient_maps is populated
postconf -e 'relay_recipient_maps = hash:/etc/postfix/relay_recipients'

postfix reload

After reloading, test with postmap -q and send a test message to confirm acceptance.

Stale or incomplete recipient map

The user exists in the authoritative directory but the Postfix map was not rebuilt after the directory changed.

# Rebuild a hash map from its source file
postmap /etc/postfix/relay_recipients

# Verify the lookup now succeeds
postmap -q user@domain hash:/etc/postfix/relay_recipients

For maps generated by a script or sync job, verify the generation process ran and check for errors in its output. Automate the rebuild on a cron schedule or trigger it from the provisioning system.

Network-backed map unresponsive or returning empty results

For LDAP or SQL-backed maps, the backend may be slow, returning unexpected empty results, or timing out.

Check the backend directly. Verify the query in the map configuration file returns results when run manually. Review connection parameters and timeouts in the map definition file (e.g., ldap-recipients.cf or mysql-virtual.cf). Ensure the LDAP or SQL server has adequate capacity and replication is current.

If the backend is intermittently slow, configure timeouts in the map definition to prevent smtpd from hanging indefinitely.

Dictionary attack (spike of rejections from one source)

A burst of “User unknown” rejections from few IPs hitting random or sequential addresses. The rejections are working correctly.

Rate-limit the attacking source and consider pre-queue filtering:

# Limit recipients per client (default: 0, disabled)
postconf -e 'smtpd_client_recipient_rate_limit = 100'

# Enable postscreen for pre-queue zombie filtering
# WARNING: Enabling postscreen changes how Postfix handles all incoming
# connections. Review postscreen(8) and configure postscreen_* parameters
# before enabling in production.
postconf -e 'postscreen_enable = yes'

postfix reload

Also consider fail2ban or equivalent IP-based blocking for sources generating excessive rejections.

Do not disable recipient validation on internet-facing systems

Setting local_recipient_maps (or relay_recipient_maps) to empty disables recipient validation for that class. Postfix will accept mail for any address, then generate bounce messages to the envelope sender. On internet-facing systems, forged sender addresses mean these bounces go to innocent third parties. This is backscatter, and it gets your IP listed on blocklists.

If you are tempted to set local_recipient_maps = to stop rejections, the correct fix is to populate the map with valid recipients, not to disable validation.

Prevention

  • Automate map rebuilds. Synchronize recipient maps from the authoritative directory on a schedule or via event-triggered updates.
  • Validate domain classification after config changes. After any change to mydestination, virtual_mailbox_domains, or relay_domains, verify each domain maps to the correct recipient validation map.
  • Monitor reject rates by category. Track “User unknown” rejections separately from relay denials, SPF failures, and other reject types.
  • Include postmap -q in health checks. Test known valid recipients as part of monitoring or pre-deployment validation.
  • Monitor network-backed map query latency. Set thresholds on LDAP and SQL query times to catch backend degradation before it causes smtpd starvation.
  • Use postscreen and rate limiting. Filter zombies before SMTP proper to prevent dictionary attacks from consuming smtpd resources.

How Netdata helps

  • Correlate reject count spikes with per-client connection rates to distinguish dictionary attacks from configuration drift. A spike from one IP at 3 a.m. is an attack; a steady increase across all senders after a config change is drift.
  • Track bounce rate alongside reject patterns. If valid recipients are being rejected, senders see bounces; sustained bounce rates above 1% indicate reputation risk.
  • Monitor smtpd process utilization against maxproc. Slow map queries hold smtpd processes open; approaching the process limit with low throughput points to backend latency, not traffic volume.
  • Alert on map-related patterns. A spike in “User unknown” rejections for a domain that should be relayed, combined with normal delivery for other domains, isolates the problem to map or address-class configuration for that domain.