Back

Dovecot Wildcard Checklist: Fallback Order and Username Format

A catch-all address on a Dovecot-backed server is one of the fastest ways to stop losing mail sent to slightly wrong local parts. It is also one of the easiest ways to quietly break real logins if the wildcard in the userdb is not ordered correctly. The failure is subtle: authentication succeeds, the session opens, and the user lands in a shared mailbox that is not theirs. The checklist below focuses on the two settings that cause most of these incidents, the fallback order of the userdb lookup and the consistency of auth_username_format.

Confirm the wildcard is a fallback, not the first match

Dovecot resolves a login in two stages. The passdb verifies credentials, then the userdb returns the home directory, UID, GID, and mail location. A wildcard entry in the userdb is a fallback rule: it should only run when no specific entry matched. If the wildcard sits first in the configuration or is provided by a passdb that returns userdb fields, every login resolves to the catch-all mailbox. The passdb check may still pass because the password is validated against the wildcard account, so nothing looks broken until a user opens their inbox and finds someone else’s mail.

The usual architecture is a static or passwd-file passdb for real accounts, a separate passdb for the catch-all, and a userdb that queries specific users before falling through. In a typical Postfix, Dovecot, and MySQL stack the userdb is a SQL query with a WHERE clause on the username. The wildcard is a second query that returns a fixed home for any name. The order matters: the specific query must return rows for real users, and the fallback query must only run when the first returns nothing. With a passwd-file userdb, the same logic applies through the wildcard line in the file, which must come after the explicit user lines.

Verify the actual resolution with doveadm. Run doveadm user user@example.com for a known account and confirm the home directory is the user’s own, not the shared catch-all path. Then run the same command for an invented address such as doveadm user zzz-random-8812@example.com. If both return the same home, the wildcard is winning. On a healthy setup the first returns a unique home and the second returns the fallback home. Repeat the check after every configuration change, because a misplaced include or a reordered passdb block can flip the result without any obvious error in the logs.

Watch the interaction between passdb and userdb wildcards

A wildcard in the passdb is a different animal from a wildcard in the userdb. Many deployments use a passdb wildcard to accept any password for the catch-all account, or to route unknown recipients into a single mailbox. That works only if the userdb still resolves specific users first. A common mistake is to copy a working catch-all configuration from one server, where the passdb and userdb were both wildcards, onto another server that has real accounts. The copied passdb then authenticates every address against the catch-all and the specific userdb never gets a chance. Check both files: the passdb should only accept the catch-all when the address is not a real account, which usually means a SQL or passwd-file lookup with an explicit fallback, not a blanket wildcard.

Logs help here. With auth_debug = yes and auth_verbose = yes, Dovecot prints which passdb and userdb returned the result for each login. A line reading “userdb lookup: user zzz-random-8812@example.com matched wildcard” for an address that should be a real user is the smoking gun. Run the debug for a few minutes, test one real login and one invented address, then turn it off. The log line names the exact passdb or userdb block, so you can trace it back to the configuration file and fix the order there rather than guessing.

Make auth_username_format strip the domain consistently

The second half of the checklist is the username format. Dovecot stores users with a username, and the local part of the email address is usually what the userdb matches on. If a login arrives as alice@example.com and the userdb stores alice, the specific lookup fails and the wildcard catches the login. Setting auth_username_format = %Ln strips the domain and lowercases the local part before the lookup. The catch is consistency: the same format must apply to every passdb and userdb, and to every protocol, or the same user will resolve differently depending on whether they connect over IMAP, POP3, or SMTP submission.

The trap is a mixed format. Suppose the SQL userdb query compares against the full address, but auth_username_format strips the domain. The specific query then returns nothing and the wildcard returns the catch-all home. The user logs in with the right password, sees the wrong mailbox, and the support ticket says “my mail is gone.” The inverse also happens: the userdb expects the local part but the format leaves the domain in place, so the specific query misses and the wildcard wins again. https://allmail.one/what-is-catch-all-email/ offers additional context worth reviewing. Either way the wildcard is not the bug, the mismatch between format and lookup is. The fix is to decide on one canonical username and apply it everywhere: the passdb query, the userdb query, and auth_username_format all use the same value.

Test the format directly. doveadm auth test alice@example.com shows the username Dovecot resolved after the format was applied. If the output shows alice while the userdb query expects alice@example.com, the mismatch is confirmed. Run the same test with the address in uppercase and with a mixed-case domain, because a format that strips the domain but does not lowercase will still miss a user who typed Alice@Example.com. The %Ln form handles both the strip and the lowercase in one step, which is why it is the usual recommendation. Whatever you choose, write it down and use it in every passdb and userdb block on the host.

Walk the full checklist before you trust the catch-all

Start at the DNS layer. MX records route inbound mail to a mail server, and the catch-all only receives anything if the MX host priority points at the server that actually runs Dovecot and accepts unknown recipients. A catch-all is configured on the mail server, not in DNS; there is no DNS record that says “accept every local part.” If the MX record points at a different host, or at a backup MX that rejects unknown recipients, the catch-all never sees the message. Verify the MX host priority, then confirm the SMTP listener on that host is accepting mail for the domain, either through a virtual alias map or through Postfix’s luser_relay or a dedicated catch-all account. The standard mail server replies with 550 no such user when no mailbox matches, and the catch-all rule replaces the failed address lookup branch, so the SMTP side and the Dovecot side must agree on which addresses are accepted.

Next, confirm what actually lands in the catch-all mailbox. The point of the setup is single mailbox collection: every unmatched local part is delivered to one place so nothing bounces. That is useful for a domain where you expect typo’d addresses, but it also means invented addresses all arrive. Catch-all domains carry risks of spam to invented addresses, and a public catch-all will collect dictionary attacks and bounce-back spam within days. Decide whether the mailbox is monitored and filtered, and whether the catch-all should be limited to a subdomain rather than the primary domain. Keep in mind that a catch-all differs from an alias, forwarding, and disposable addresses. An alias is an explicit mapping from one address to another, forwarding relays to an external mailbox, and a throwaway address is created per signup and then discarded. The catch-all is the fallback for everything that has no other rule, so it should be the last resort in the delivery chain, not the first.

Then check the outbound side, which is separate from all of this. SPF, DKIM and DMARC govern outbound mail only. They have no effect on whether the catch-all accepts inbound mail, and changing them will not fix a wildcard that is matching real users. If a user reports that mail they send is landing in spam, that is an authentication issue for the sending domain, not a catch-all issue. Keep the two problems separate in your head and in your ticket queue. The catch-all setup requires DNS pointing at the right host and a server accepting unknown recipients; outbound authentication requires valid SPF, DKIM, and DMARC records. Mixing them up wastes time and often leads to someone loosening the DMARC policy to “fix” a delivery problem that was never about outbound mail.

Finally, run the resolution tests in order. For a real user, doveadm user must return their own home, and doveadm auth test must show the canonical username after auth_username_format is applied. For an invented address, doveadm user must return the catch-all home, and a test message must arrive in the catch-all mailbox. For an address that is an explicit alias, the message must land at the alias target, not the catch-all. If any of these three cases resolve to the wrong place, the wildcard order or the username format is wrong. Do the tests on every protocol the users actually use, because IMAP and SMTP submission can resolve the same address through different passdb paths.

The checklist is short but it has to be run in order, because a failure at the DNS or SMTP layer will look like a Dovecot problem and send you chasing the wrong setting. Confirm the MX host priority, confirm the SMTP listener accepts unknown recipients, confirm the userdb wildcard is a fallback after specific users, and confirm auth_username_format strips the domain the same way in every block. If the wildcard is a fallback and the format is consistent, real users land in their own mailboxes and invented addresses land in the catch-all. If either condition fails, every login lands in the shared catch-all and the mail quietly piles up in the wrong place. Test with doveadm after every change, keep the debug logging on long enough to see which block matched, and write down the canonical username format so the next person who edits the configuration does not undo it.

newadminuser1
newadminuser1
http://www.example.com/