Dovecot Wildcard Setup: Configure the userdb, Not the Mailbox
A catch-all email setup in Dovecot fails for one predictable reason: the wildcard is placed in the mailbox layer instead of the userdb. The result looks fine in a test with one invented address, then quietly creates a brand-new Maildir for every random local part that arrives, and the disk fills. Understanding how catch-all email works starts with the lookup order. The MTA accepts the message, then asks Dovecot’s userdb to resolve the recipient address to a user, home directory and mail_location. The mailbox is created only after that lookup succeeds.
This walkthrough shows the correct sequence for a catch-all domain on Dovecot: a wildcard entry in a static userdb or passwd-file that maps *@example.com to one account and one shared mail_location, so every unmatched local part lands in a single catch-all mailbox. You will see why the rule belongs in the userdb, how to test it with doveadm and real deliveries, and which monitoring checks catch a misconfiguration before it eats your storage. Where relevant, the guide notes how a catch-all differs from an alias, a forward or a set of throwaway addresses, since mixing those mechanisms is the second most common source of confusion.
The setup assumes DNS is already sane: MX records route inbound mail to the right host, and the server is configured to accept unknown recipients rather than replying with 550 no such user. SPF, DKIM and DMARC govern outbound mail authentication only and play no part in whether the wildcard resolves. With those pieces in place, the rest is a small number of config lines and a verification pass that takes about five minutes.
How a Catch-All Email Works in Dovecot: Userdb Lookup vs. Mailbox Creation
Every message that arrives for a domain follows the same path before it reaches a disk. The sending server looks up the MX records for the domain, connects to the highest-priority host, and hands over the envelope. The receiving MTA then asks a simple question: does a local mailbox exist for this recipient? A catch-all email setup changes the answer to that question. Instead of rejecting unknown recipients, the server accepts them and routes them somewhere defined. What matters for Dovecot administrators is where that decision is actually made.
Take the address [email protected]. Dovecot never sees the SMTP conversation. By the time the message reaches Dovecot, the MTA (Postfix, Exim, or similar) has already accepted it and passed it to the LDA or LMTP service. Dovecot’s job is to resolve the recipient string into three things: a username, a home directory, and a mail location. That resolution is the userdb lookup. The wildcard belongs in this lookup, not in the filesystem and not in the mailbox itself.
Here is the resolution path in order:
- The MTA accepts the message for [email protected] and queries Dovecot over LMTP or calls
dovecot-lda. - Dovecot strips the domain (if configured) and runs the userdb lookup on the local part.
- If a userdb entry matches, Dovecot returns the user’s home and
mail_location, then delivers into that Maildir or mdbox. - If no entry matches, the lookup fails and delivery is rejected. With a standard mail server configuration, the sender gets a bounce message containing 550 no such user.
A catch-all mailbox is the destination that collects every unmatched address in one place. A catch-all domain is the domain configured to behave this way: every message sent to it is accepted regardless of the local part. The catch-all rule replaces the failed address lookup branch. Rather than returning “no such user,” the userdb returns a valid user, and the message lands in a shared mailbox. The wildcard pattern (*@example.com or just *) is a userdb key. It is configured on the mail server, not in DNS. MX records route inbound mail to a mail server; they say nothing about which recipients that server will accept.
The common misconception is that you create a mailbox called “catch-all” and the server somehow knows to dump unknown mail there. A mailbox is just a directory of message files. It has no concept of address matching. If you create the mailbox but leave the userdb untouched, unknown recipients still fail the lookup and still bounce. Conversely, if you add the wildcard userdb entry but point it at a home directory that does not exist, Dovecot will happily create it and deliver, which is exactly the behavior you want, provided the entry is a single static user and not a per-address expansion.
That last point is the trap. Some configurations derive the username from the recipient address, so [email protected] becomes user random123 with its own home directory. Each new invented local part spawns a new mailbox. Spam sent to throwaway addresses then fills the disk one directory at a time. The fix is a static userdb entry that maps every unmatched address to one fixed user, which is the subject of the next section.
Catch-All vs. Alias vs. Forwarding: Choosing the Right Mechanism
A catch-all email, an alias, and a forward all solve the same surface problem (mail sent to an address that does not have its own mailbox), but they take completely different routes inside the mail system. Confusing them is the most common reason a Dovecot wildcard ends up storing gigabytes of junk or silently dropping legitimate mail.
A catch-all email configuration makes the mail server accept every message sent to a domain, including addresses nobody created. In Dovecot terms, the wildcard lives in the userdb: a lookup for any local part resolves to one account. The catch-all mailbox then collects all unmatched addresses in a single place, so sales@, info-2024@, and x9f2k@ all land in the same Maildir. The decision happens at the recipient address filter stage, before any mailbox is opened, and it is configured on the mail server, not DNS. MX records only route inbound mail to the host; they know nothing about which local parts exist.
An alias is a fixed, explicit mapping. You declare that [email protected] delivers to [email protected], and nothing else changes. Aliases are usually kept in a database table, a Dovecot passwd-file, or the MTA (Postfix virtual_alias_maps, Exim redirect routers). They cost nothing in storage because no extra mailbox exists, and they fail closed: an address that is not listed gets the standard reply of 550 no such user. That is exactly the behavior a catch-all removes.
Forwarding is delivery to a different destination, either another local mailbox or an external address. In Postfix this is a virtual alias or a .forward file; in Dovecot it is often a sieve redirect. Forwarding preserves the original envelope recipient in most setups, which matters for SPF alignment on the relayed copy. Storage cost is zero locally, but you now depend on the downstream provider’s spam filtering and rate limits, and a bounce message comes back to your server, not the sender’s inbox.
| Mechanism | Where configured | Accepts unknown local parts | Local storage |
|---|---|---|---|
| Catch-all | Dovecot userdb (or MTA fallback rule) | Yes, every address in the domain | One shared mailbox, grows with all inbound junk |
| Alias | MTA map or Dovecot passwd-file | No, only listed addresses | None beyond the target mailbox |
| Forwarding | MTA map, sieve redirect, .forward |
No, only listed addresses | None locally |
The practical rule: use aliases for addresses you control and can enumerate, forwarding when mail should leave the server, and a catch-all only when you genuinely need to capture mail for addresses invented by others, such as typo recovery, legacy domains after a migration, or per-signup throwaway addresses that all funnel into one inbox. https://allmail.one/what-is-catch-all-email/ covers this in more depth. A catch-all domain carries real risk: spammers probe invented local parts constantly, and every hit is accepted. SPF, DKIM and DMARC govern outbound mail only, so authentication will not reduce that inbound volume.
One more distinction matters for the setup in the next section. A disposable or throwaway address service is a third-party product, not a server feature. If you want that behavior on your own domain, the catch-all rule replaces the failed address lookup branch in Dovecot, which is why the wildcard belongs in the userdb query and not in a per-mailbox setting. Aliases and forwards keep the lookup strict; the catch-all deliberately loosens it.
Configuring the Wildcard in the Userdb, Not the Mailbox
The wildcard lives in the userdb, not in a mailbox. Dovecot resolves every incoming recipient in two stages: first it asks the userdb “which user owns this address?”, then it builds a mailbox from that user’s home and mail_location. If the userdb has no match, the lookup fails and the message is rejected. A catch-all rule replaces that failed address lookup branch with a successful one that points at a single existing user. Nothing about the mailbox itself changes.
Here is the failure mode people hit. Suppose you add a catch-all entry in Postfix or your MTA that accepts mail for the domain, but you never teach Dovecot’s userdb about unknown recipients. Every random local part, whether it is [email protected] or [email protected], gets a fresh lookup, fails, and either bounces or, worse, gets auto-created by a permissive userdb template. If you use a pattern that derives the home directory from the address, each invented recipient spawns a new Maildir. Spam bots invent addresses constantly, so the disk fills with thousands of empty and near-empty mailboxes within days.
The fix is a static userdb entry that maps every address in the domain to one home and one mail_location. In /etc/dovecot/dovecot.conf, define a passdb for authentication and a static userdb for the catch-all:
- Keep your real mailboxes on a passwd-file or SQL passdb as usual, so named users still authenticate normally.
- Add a static userdb block that returns the catch-all home, uid, gid, and mail_location for any address in the domain. Set
args = username_format=%uso the original recipient is preserved for logging. - If you prefer a file-based approach, use a passwd-file with a wildcard line such as
*@example.com:{PLAIN}secret:5000:5000::/var/mail/catchall::and reference it from both passdb and userdb. The wildcard local part matches anything. - Set
mail_location = maildir:/var/mail/catchall/Maildiron that user so every accepted address lands in the same store. - Point your MTA’s virtual alias or transport at the catch-all user, or let the MTA accept the domain and hand delivery to Dovecot LMTP with the original recipient intact.
Why a Static Userdb Prevents Mailbox Explosion
A static userdb returns the same home for every query, so Dovecot never creates a directory per recipient. The catch-all mailbox collects all unmatched addresses in one place, and you can filter them later with recipient address filters or sieve rules. Contrast that with an unconfigured wildcard: the userdb template /var/mail/%u expands to a new path for each invented local part, and each path becomes a mailbox. One spam run against a domain with no wildcard handling can produce tens of thousands of directories. With the static entry, the same run produces one Maildir and a lot of messages inside it.
Note where the configuration lives. The catch-all is configured on the mail server, not DNS. MX records route inbound mail to the host, but they do not decide acceptance. SPF, DKIM and DMARC govern outbound mail only and have no bearing on whether an unknown recipient is accepted. Keep the wildcard in the userdb, leave DNS alone, and the catch-all stays a lookup rule rather than a mailbox factory.
Testing the Catch-All Setup and Guarding Against Disk-Filling Mailboxes
Before you trust the wildcard, verify it. Start with a direct lookup against the userdb, bypassing SMTP entirely. Run doveadm user ‘[email protected]’. A correctly configured catch-all returns the shared home, uid, gid and mail field, and the same output appears no matter what local part you pass. If the command errors with “user not found”, the wildcard entry is missing or the userdb driver order is wrong: Dovecot checks the passwd-file or static userdb first, and a plain passdb match on the exact address can shadow the wildcard. Check doveadm user -f home ‘[email protected]’ to confirm the home path is the one shared by all unmatched addresses.
Next, prove the SMTP side accepts unknown recipients. Send test messages to three invented local parts, ideally from an external account so you exercise the full inbound path rather than a local submission:
- doveadm user ‘[email protected]’ to confirm the lookup resolves.
- Send to [email protected], [email protected] and [email protected] via your normal MTA.
- Watch the maillog for “saved mail to INBOX” lines referencing the shared home, and check for any 550 no such user rejections, which mean the wildcard is not applied at the SMTP layer.
Then confirm delivery physically. Count messages in the shared Maildir: ls /var/mail/example.com/catchall/new | wc -l before and after the test, and check that no new directory appeared under the mail root for the random local parts. If you see a fresh Maildir per address, the wildcard is not winning the userdb lookup, and every random sender will keep creating mailboxes until the disk fills. A quick sanity check is find /var/mail/example.com -maxdepth 1 -type d | wc -l; the number should stay flat across test deliveries.
Set up monitoring so a misconfiguration surfaces before it costs you. A cron job running every 15 minutes can flag three conditions: mailbox count above an expected baseline, disk usage on the mail volume above 80 percent, and any new top-level directory in the mail root. Something like find /var/mail/example.com -maxdepth 1 -mindepth 1 -type d | wc -l piped into a comparison against a stored threshold works fine. Also watch the mail log for repeated “userdb lookup failed” or “unknown user” lines, which indicate the wildcard stopped resolving after a config reload.
Finally, remember that a catch-all domain attracts spam to invented addresses. Set a retention policy on the shared mailbox, for example deleting messages older than 30 days from a dedicated catch-all folder, and keep a separate quota on that account so a spam flood cannot starve legitimate users. Verify with doveadm quota get -u [email protected] that limits are enforced, and test one more delivery to a random local part after any config change. If the message lands in the shared Maildir and the mailbox count is unchanged, the setup is doing what a catch-all mailbox should: collecting every unmatched address in one place.
Before you walk away from the terminal, run one last end-to-end check: send a message to a deliberately absurd local part such as [email protected], then confirm with doveadm mailbox list -u [email protected] that it landed in the existing INBOX and not in a freshly created folder. Follow it with find /var/mail/yourdomain.com -maxdepth 1 -type d | wc -l after ten or so test deliveries. The count should not move. If it does, your wildcard is resolving somewhere other than the entry you configured, and the usual culprits are a leftover passwd-file row, a static userdb block that never got reloaded, or an auth cache still holding the old lookup result. Restart Dovecot with systemctl reload dovecot, clear the auth cache, and test again before you point real traffic at the domain.
The next step is monitoring, because a catch-all domain quietly becomes a spam magnet: invented local parts get harvested from old signup forms and directory dumps, and every one of those messages lands in your single mailbox collection. Set a daily check that counts messages in the catch-all INBOX and alerts above a threshold you actually expect, and keep an eye on the disk quota on the home directory behind that shared mail_location. A cron job running doveadm quota get -u [email protected] plus a simple du -sh on the Maildir is enough to catch runaway growth in a week rather than a month. When the volume gets noisy, add recipient address filters in Sieve or the MTA so obvious junk is dropped at SMTP time, and document the wildcard entry in your config management so the next admin knows why *@yourdomain.com exists in the userdb and that it is intentional.