From 01ae62e6214c46c54bed2b853a44d12536b92450 Mon Sep 17 00:00:00 2001 From: MajorLinux Date: Sun, 7 Jun 2026 05:06:43 -0400 Subject: [PATCH] troubleshooting: Dovecot phantom mailboxes from .dovecot.lda-dupes (mail_home overlapping maildir root) Document the majormail 2026-06-07 incident: when userdb home == maildir root, the LDA/Sieve duplicate database (.dovecot.lda-dupes + .locks) lands inside the mail store and the maildir lister exposes it as phantom mailboxes ("dovecot.lda-dupes"), logging stat(.../tmp) "Not a directory". Fix: point home at a non-dotted subdir. Wired into the troubleshooting index and SUMMARY. --- 05-troubleshooting/index.md | 1 + ...ail-home-maildir-root-phantom-mailboxes.md | 111 ++++++++++++++++++ SUMMARY.md | 1 + 3 files changed, 113 insertions(+) create mode 100644 05-troubleshooting/networking/dovecot-mail-home-maildir-root-phantom-mailboxes.md diff --git a/05-troubleshooting/index.md b/05-troubleshooting/index.md index 41e16a2..1969a4d 100644 --- a/05-troubleshooting/index.md +++ b/05-troubleshooting/index.md @@ -16,6 +16,7 @@ Practical fixes for common Linux, networking, and application problems. - [firewalld: Mail Ports Wiped After Reload](networking/firewalld-mail-ports-reset.md) - [Dovecot IMAP Clients Fail to Sync: vsz_limit OOM from a Bloated Index Log](networking/dovecot-imap-oom-vsz-limit-bloated-index.md) - [Postfix header_checks Can't Act on Milter-Added Headers (Use Sieve)](networking/postfix-header-checks-vs-milter-headers.md) +- [Dovecot Phantom Mailboxes from .dovecot.lda-dupes (mail_home Overlapping the Maildir Root)](networking/dovecot-mail-home-maildir-root-phantom-mailboxes.md) - [Tailscale SSH: Unexpected Re-Authentication Prompt](networking/tailscale-ssh-reauth-prompt.md) - [iOS Tailscale Clients Report HostName="localhost" — Breaks /etc/hosts Generators](networking/tailscale-status-json-hostname-localhost-ios.md) - [rsync over Tailscale: Hung in TCP Teardown After Transfer Completes](networking/rsync-tailscale-teardown-stall.md) diff --git a/05-troubleshooting/networking/dovecot-mail-home-maildir-root-phantom-mailboxes.md b/05-troubleshooting/networking/dovecot-mail-home-maildir-root-phantom-mailboxes.md new file mode 100644 index 0000000..e39cf1a --- /dev/null +++ b/05-troubleshooting/networking/dovecot-mail-home-maildir-root-phantom-mailboxes.md @@ -0,0 +1,111 @@ +--- +title: "Dovecot Phantom Mailboxes from .dovecot.lda-dupes (mail_home Overlapping the Maildir Root)" +domain: troubleshooting +category: networking +tags: [dovecot, maildir, mail_home, sieve, lda-dupes, duplicate-database, pigeonhole, phantom-mailbox] +status: published +created: 2026-06-07 +updated: 2026-06-07 +--- +# Dovecot Phantom Mailboxes from `.dovecot.lda-dupes` (mail_home Overlapping the Maildir Root) + +Dovecot starts logging errors like this on mailbox LIST, and `doveadm mailbox list` grows phantom mailboxes named after Dovecot's own control files: + +``` +imap(user@example.com): Error: maildir: stat(/var/vmail/example.com/user/.dovecot.lda-dupes/tmp) failed: Not a directory +``` + +``` +$ doveadm mailbox list -u user@example.com +INBOX +… +dovecot +dovecot.lda-dupes +dovecot.lda-dupes.locks +``` + +> Hit on **majormail** (2026-06-07), the day after switching the global spam Sieve to `redirect`. Mail delivery was unaffected — purely log noise plus phantom folders a client could see on `LIST "*"`. + +## Why + +The LDA/Sieve **duplicate database** (`.dovecot.lda-dupes`, plus a `.dovecot.lda-dupes.locks` lock dir) is created in the user's **home** directory. Per the Dovecot maintainer, its location strictly follows the user's home — it is *not* separately configurable. + +If `mail_home` (the userdb `home` field) is set equal to the **maildir root** (`mail_path`), those control files get written *inside* the mail store: + +``` +mail_path = /var/vmail/%{user|domain}/%{user|username} # maildir root +userdb static { fields { home = /var/vmail/%{user|domain}/%{user|username} } } # SAME path — the bug +``` + +The maildir++ layout treats every `.`-prefixed entry in the root as a mailbox folder. So: + +- `.dovecot.lda-dupes` (a **file**) → lister stats `.dovecot.lda-dupes/tmp` → **"Not a directory"** (cosmetic, logged every LIST). +- `.dovecot.lda-dupes.locks` (a **directory**) → opened as a maildir, auto-populated with `cur/new/tmp/dovecot-uidlist/dovecot.index.log`, and exposed as a real phantom mailbox. + +The trigger is anything that exercises duplicate tracking — Sieve `redirect` (loop-guard), `vacation`, or the `duplicate` test. A pure `fileinto` setup never creates the db, which is why the error can appear suddenly after a Sieve change. + +## How to confirm + +```bash +# Phantom mailboxes named after the control files: +doveadm mailbox list -u user@example.com | grep -E '^dovecot' + +# Is home the SAME as the maildir root? (the root cause) +doveadm user user@example.com | grep -E 'home|mail_path' +# home /var/vmail/example.com/user <- equals mail_path == bug +# mail_path /var/vmail/example.com/user + +# The offending control files living inside the maildir root: +ls -la /var/vmail/example.com/user/.dovecot.lda-dupes* +# -rw------- … .dovecot.lda-dupes (regular file — the dedup db) +# drwx------ … .dovecot.lda-dupes.locks (dir — the lock dir, mis-listed) +``` + +## Fix + +Point `home` at a path **separate from the maildir root**. The cleanest low-risk option is a **non-dotted subdir** of the user dir, so `mail_path` stays put and **no mail migration** is needed (a dotted name would just become another phantom folder): + +```diff + userdb static { + fields { + uid = vmail + gid = vmail +- home = /var/vmail/%{user|domain}/%{user|username} ++ home = /var/vmail/%{user|domain}/%{user|username}/home + } + } +``` + +Then deploy and clean up the stale artifacts: + +```bash +# 1. Deploy the config change, restart/reload Dovecot. + +# 2. Confirm home moved: +doveadm user user@example.com | grep home # -> /var/vmail/example.com/user/home + +# 3. Remove the stale dupe-db + the cached list index from the maildir root +# (all regenerable): +cd /var/vmail/example.com/user/ +rm -rf .dovecot.lda-dupes .dovecot.lda-dupes.locks dovecot.list.index dovecot.list.index.log + +# 4. Pre-create the new home (so the first dupe-db write can't fail): +install -d -o vmail -g vmail -m 700 /var/vmail/example.com/user/home + +# 5. Verify: +doveadm mailbox list -u user@example.com | grep -E '^dovecot' || echo CLEAN +``` + +The duplicate db now regenerates under `…/user/home/`, where the maildir lister never looks. + +## Gotchas + +- **`mail_home` follows userdb.** A userdb-returned `home` field overrides the global `mail_home` setting, so fix it where userdb defines it (here, `userdb static { fields { home = … } }`). +- **What else keys off `~`:** personal Sieve (`~/.dovecot.sieve`, `~/sieve`), `mail_attribute_dict`, and some quota backends. Before moving home, confirm none of those hold live data in the old location (`ls -a` the maildir root). A *global* spam Sieve at a fixed path (`/etc/dovecot/sieve/global/…`) is unaffected. +- **Indexes** default to `mail_path`, not home, so moving home doesn't touch `dovecot.index*`. +- **Don't trust a local-injection test** to exercise Sieve `redirect`: Postfix `cleanup` header_checks may intercept it first, and `dovecot-lda` may not apply the same before-script as LMTP. Verify the relocation at the authoritative level (`doveadm user` home), since the db location is home-relative by design. + +## Related + +- [[postfix-header-checks-vs-milter-headers]] — the spam-routing migration that introduced the Sieve `redirect` (and thus the dupe db) on majormail. +- Upstream: Dovecot mailing-list thread "Change location where .dovecot.lda-dupes* file/dir are created" — maintainer confirms the db follows the user's home. diff --git a/SUMMARY.md b/SUMMARY.md index d65f69f..ed771d5 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -82,6 +82,7 @@ updated: 2026-05-15T09:00 * [firewalld: Mail Ports Wiped After Reload](05-troubleshooting/networking/firewalld-mail-ports-reset.md) * [Dovecot IMAP Clients Fail to Sync: vsz_limit OOM from a Bloated Index Log](05-troubleshooting/networking/dovecot-imap-oom-vsz-limit-bloated-index.md) * [Postfix header_checks Can't Act on Milter-Added Headers (Use Sieve)](05-troubleshooting/networking/postfix-header-checks-vs-milter-headers.md) + * [Dovecot Phantom Mailboxes from .dovecot.lda-dupes (mail_home Overlapping the Maildir Root)](05-troubleshooting/networking/dovecot-mail-home-maildir-root-phantom-mailboxes.md) * [Tailscale SSH: Unexpected Re-Authentication Prompt](05-troubleshooting/networking/tailscale-ssh-reauth-prompt.md) * [ssh.socket Unreachable After Reboot (Tailscale Race Condition)](05-troubleshooting/networking/ssh-socket-tailscale-race-condition.md) * [Fail2ban & UFW Rule Bloat Cleanup](05-troubleshooting/networking/fail2ban-ufw-rule-bloat-cleanup.md)