ایمیل تحویل داده میشود ولی Inbox خالی است؛ تلهی mail_location
ایمیل در لاگ Postfix «تحویل داده» میشود اما کاربر هنگام اتصال IMAP صندوق خالی میبیند. علت رایج، اختلاف مسیر تحویل میان Postfix و mail_location در Dovecot است.

مسئله: ایمیل «تحویل داده شد»، ولی صندوق خالی است
سناریو را ببینید. کاربر میگوید ایمیلی به من نمیرسد، یا ایمیلهایی که ارسال شدهاند در Inbox نیستند. سراغ /var/log/mail.log میروید و میبینید همهچیز از نظر Postfix سالم است:
postfix/lmtp[29341]: 4Xk2...: to=<ali@example.com>, relay=example.com[/private/dovecot-lmtp], delay=0.12, status=sent (250 2.0.0 <ali@example.com> Saved)
یا اگر LDA در مسیر باشد:
postfix/local[29344]: 4Xk2...: to=<ali@example.com>, relay=local, delay=0.21, status=sent (delivered to maildir)
ولی به محض اینکه کاربر با Thunderbird یا Roundcube وصل میشود، INBOX خالی است. در /var/log/dovecot.log معمولاً یکی از این خطوط را میبینید:
imap(ali@example.com)<...>: Error: chdir(/home/ali/Maildir) failed: No such file or directory
یا این:
Error: open(/var/vmail/example.com/ali/Maildir/dovecot-uidlist) failed: Permission denied
حتی ممکن است هیچ خطایی هم نبینید؛ Dovecot یک صندوق خالی میسازد و همهچیز از بیرون سالم به نظر میرسد. نکتهی کلیدی این است: Postfix پیام را در یک مسیر مینشاند و Dovecot مسیر دیگری را نگاه میکند. کاربر نه خطا میبیند، نه ایمیل.
علت ریشهای: دو جفت کلید که با هم هماهنگ نیستند
در یک سرور ایمیل، تحویل پیام و ارائهی IMAP دو لایهی جداست. Postfix مسئول گرفتن پیام است و Dovecot مسئول نمایش آن. اگر این دو دربارهی «محل صندوق» توافق نداشته باشند، پیام در یک جا مینشیند و کاربر جای دیگری را نگاه میکند. سه منبع تفرق را بررسی کنید:
- mbox در برابر Maildir: Postfix با
home_mailbox = Maildir/درmain.cfبه~/Maildir/مینویسد، ولی مقدار پیشفرض Dovecot روی بعضی توزیعهاmbox:~/mail:INBOX=/var/mail/%uاست. دو مسیر کاملاً متفاوت. - مبنای virtual_mailbox: در حالت کاربران مجازی، Postfix با
virtual_mailbox_baseبه یک مسیر مینویسد و Dovecot باmail_locationمسیر را میخواند. اگر یکی%d/%nو دیگری%uباشد، مسیرها از هم جدا میشوند. - mailbox_command: اگر
mailbox_commandتنظیم شده باشد (مثلاًdovecot-ldaیاprocmail)، تحویل به آن سپرده میشود وhome_mailboxعملاً بیاثر است. خیلیها هر دو را با هم دارند و از همینجا سردرگم میشوند.
و یک تلهی ظریف که خیلی وقتها ندیده میماند: lmtp_save_to_detail_mailbox = yes در Dovecot. اگر این گزینه فعال باشد، پیامهای user+tag@example.com به یک زیرپوشه میروند، نه به INBOX. کاربر فکر میکند ایمیل نیامده، در حالی که پیام داخل INBOX/tag نشسته است.
در محیطهایی که SELinux یا AppArmor فعال است، یک لایهی سوم هم اضافه میشود: حتی اگر مسیر درست باشد، پروفایل امنیتی ممکن است اجازهی نوشتن روی /var/vmail را ندهد و علائم دقیقاً به شکل «صندوق خالی» دیده شود. در آن حالت audit.log یا dmesg را هم چک کنید.
راهحل گامبهگام
گام ۱: کشف لایهی تحویل
اول ببینید Postfix واقعاً چه مسیری را انتخاب میکند:
postconf -n | grep -E "home_mailbox|virtual_transport|mailbox_command|virtual_mailbox_base|virtual_mailbox_maps"
سه حالت رایج را میبینید. اگر virtual_transport = lmtp:unix:private/dovecot-lmtp است، کل تحویل به Dovecot سپرده شده و Postfix خودش به دیسک دست نمیزند. اگر virtual_transport = virtual است، Postfix خودش مینویسد. اگر mailbox_command تنظیم شده، یک LDA جدا وسط ماجراست و باید در همان مسیر مورد توافق هر دو طرف بنویسد.
گام ۲: Dovecot چه مسیری را انتظار دارد؟
doveconf -n | grep -E "mail_location|mail_home|mail_uid|mail_gid"
doveadm user ali@example.com
دستور doveadm user مستقیماً home و mail_location را نشان میدهد. یک نمونه خروجی:
userdb:
ali@example.com
home: /var/vmail/example.com/ali
mail: maildir:/var/vmail/example.com/ali/Maildir
uid: 5000
gid: 5000
این خروجی، مرجع شما است. هر مسیری که Dovecot اینجا اعلام میکند باید با مسیری که Postfix پیام را در آن مینشاند، یکسان باشد.
گام ۳: همترازسازی در حالت virtual users (توصیهشده)
سالمترین الگو در محیط عملیاتی این است که تحویل را به LMTP بسپارید تا Dovecot خودش مسیر را تعیین کند و امکان اختلاف از اساس حذف شود.
در /etc/postfix/main.cf:
virtual_mailbox_domains = example.com
virtual_mailbox_maps = hash:/etc/postfix/vmailbox
virtual_transport = lmtp:unix:private/dovecot-lmtp
در /etc/postfix/vmailbox فقط وجود کاربر را تعریف کنید؛ خودِ مسیر را Dovecot میسازد:
ali@example.com example.com/ali/
سپس:
postmap /etc/postfix/vmailbox
systemctl reload postfix
در سمت Dovecot، فایل /etc/dovecot/conf.d/10-mail.conf:
mail_location = maildir:/var/vmail/%d/%n/Maildir
mail_uid = vmail
mail_gid = vmail
اگر userdb از SQL میآید، ستون home و mail را دقیقاً با همان الگو همتراز کنید:
SELECT '/var/vmail/' || maildir AS home,
'maildir:/var/vmail/' || maildir || '/Maildir' AS mail,
5000 AS uid, 5000 AS gid
FROM mailbox
WHERE username = '%u'
گام ۴: همترازی در حالت system users
اگر کاربران واقعی سیستم هستید و میخواهید Maildir استفاده کنید، هر دو طرف را روی یک مسیر ثابت قفل کنید. در main.cf:
home_mailbox = Maildir/
در 10-mail.conf:
mail_location = maildir:~/Maildir
برعکس، اگر مایل به استفاده از mbox هستید (که در محیطهای تولیدی توصیه نمیشود)، home_mailbox را حذف کنید و مقصد پیشفرض /var/mail/%u را صراحتاً در Dovecot اعلام کنید. هرگز هر دو حالت را همزمان فعال نگذارید.
گام ۵: مالکیت و مجوزها
حتی اگر مسیرها یکی باشند، مالکیت اشتباه همان علامت را میدهد. در حالت virtual:
chown -R vmail:vmail /var/vmail
chmod -R 0700 /var/vmail/example.com
در حالت system users، مطمئن شوید /home/ali حداقل 0711 است و ~/Maildir متعلق به خود کاربر با 0700:
chmod 0711 /home/ali
chown -R ali:ali /home/ali/Maildir
گام ۶: انتقال پیامهای قدیمی از مسیر قبلی
قبل از هر چیز یک بکاپ بگیرید، سپس محتوای مسیر قدیمی را با rsync منتقل کنید (نه mv؛ چون ممکن است همزمان نوشتن در جریان باشد):
systemctl stop postfix dovecot
rsync -aHAX --info=progress2 /var/mail/ali/ /home/ali/Maildir/
chown -R ali:ali /home/ali/Maildir
systemctl start dovecot postfix
اگر مسیر قبلی mbox بود، از ابزار تبدیل استفاده کنید:
doveadm import -u ali@example.com mbox:/var/mail/ali INBOX
بررسی اینکه مشکل واقعاً حل شده است
سه سطح تست انجام دهید. سطح اول، احراز هویت و مسیر:
doveadm auth test ali@example.com
doveadm user ali@example.com
سطح دوم، خودِ صندوق:
doveadm mailbox list -u ali@example.com
doveadm search -u ali@example.com mailbox INBOX ALL
اگر تعداد پیامها در خروجی doveadm search با انتظار شما همخوانی داشت، سطح سوم: یک ایمیل آزمایشی بفرستید و لاگها را رصد کنید:
swaks --to ali@example.com --from test@example.com --server 127.0.0.1
tail -f /var/log/mail.log /var/log/dovecot.log
در لاگ Postfix انتظار دارید ببینید:
status=sent (250 2.0.0 <ali@example.com> Saved)
و بلافاصله پس از آن در Dovecot:
lmtp(ali@example.com): ...: msgid=<...>: saved mail to INBOX
اگر خط «saved mail to INBOX» را دیدید، کار تمام است. حالا با یک کلاینت IMAP واقعی وارد شوید و مطمئن شوید پیام دیده میشود. اگر کاربر از دو کلاینت استفاده میکند (مثلاً webmail و موبایل) و یکی خالی است، احتمالاً یکی از کلاینتها با +detail یا با پیشوند متفاوت وصل میشود. یک بار جستوجو را روی همهی پوشهها بزنید تا ببینید پیام دقیقاً کجا نشسته است:
doveadm search -u ali@example.com mailbox '*' ALL
پیشگیری و بهترین روشها
- یک منبع حقیقت انتخاب کنید. در محیط با کاربران مجازی، LMTP تنها منبع حقیقت است. Postfix نباید هیچگاه مستقیماً به دیسک بنویسد. این کار اختلاف مسیر را از اساس حذف میکند.
- mail_location را در IaC تست کنید. یک تسک ادواری با Ansible یا Puppet که
doveadm userرا برای چند کاربر نمونه اجرا و خروجی را با مسیر تحویل مورد انتظار مقایسه کند، این تله را همان روز کشف میکند، نه بعد از یک هفته سکوت. - تست smoke پس از نصب و مهاجرت. یک اسکریپت با
swaksکه ایمیلی بفرستد و بلافاصله باdoveadm searchوجودش را تأیید کند، به شما سیگنال قطعی میدهد؛ نه «به نظر میرسد کار میکند». - از تعارض پیکربندی پرهیز کنید. اگر
mailbox_commandتنظیم شده،home_mailboxعملاً بیاثر است و فقط ذهن را گمراه میکند. یکی را حذف کنید. - پیش از سوییچ DNS، مسیرها را در staging همگام کنید. تغییر پیشفرض
mail_locationدر نسخههای جدید Dovecot یک سورپرایز بیصداست. - مالکیت و مجوزها را جزو پیکربندی حساب کنید. بخش زیادی از خطاهای «صندوق خالی» در واقع
Permission deniedروی~/Maildirهستند که فقط به شکل خالیبودن خود را نشان میدهند؛audit.logو SELinux را هم فراموش نکنید.
خلاصهی حرف: وقتی Postfix میگوید «delivered»، کار تمام نشده است. تا وقتی Dovecot همان مسیر را نبیند، کاربر هیچچیز نمیبیند. با LMTP تحویل بدهید، mail_location را ثابت و صریح نگه دارید و تحویل end-to-end را در استیجینگ تست کنید — نه با حدس.
نویسنده
تیم محتوای کلودیکپ
تیم فنی و محتوای کلودیکپ؛ مهندسانی که هر روز با DevOps، Kubernetes و زیرساخت ابری کار میکنند و تجربههایشان را اینجا مینویسند.
سوالات متداول این مقاله
چون تحویل و نمایش دو لایهی جداست. Postfix پیام را در مسیری مینویسد و Dovecot مسیر دیگری را برای IMAP میخواند. با doveadm user بفهمید Dovecot چه home و mail_locationای انتظار دارد و آن را با مسیر واقعی تحویل همتراز کنید.
mbox همهی پیامها را در یک فایل نگه میدارد و در محیطهای پرترافیک باعث قفلشدن و ریسک خرابی میشود. Maildir هر پیام را در یک فایل جدا میگذارد، سازگاری بهتری با IMAP و ابزارهای موازی دارد و برای سرورهای امروزی انتخاب پیشفرض است.
با doveadm user ali@example.com که مستقیماً home و mail_location را چاپ میکند. برای بررسی فولدرها هم از doveadm mailbox list و doveadm search استفاده کنید تا ببینید پیامها دقیقاً در کدام مسیر نشستهاند.
کلودیکپ در این زمینه چه کمکی میکند؟
راهاندازی میل سرور اختصاصی و ایمیل سازمانی (Self-Hosted)
ایمیل سازمانی با دامنه خودتان روی سرور داخلی؛ امن، بدون محدودیت تعداد صندوق، قابل استفاده در شرایط اینترنت ملی و آماده برای ارسال خبرنامه و ایمیل مارکتینگ.
مشاهده جزئیات خدمتخدمات مدیریتشده DevOps
برونسپاری کامل عملیات زیرساخت به تیمی متخصص، با پشتیبانی مستمر.
مشاهده جزئیات خدمتراهاندازی فضای ابری اختصاصی سازمان با Nextcloud (Self-Hosted)
همه فایلها و اسناد سازمان در فضای ابری اختصاصی خودتان؛ کاملاً امن، با پشتیبانگیری خودکار و قابل دسترس در هر زمان و از هر دستگاه. جایگزین داخلی Google Drive و Dropbox، پایدار حتی در شرایط اینترنت ملی.
مشاهده جزئیات خدمت
کران Nextcloud در Docker: چرا جابهای پسزمینه اجرا نمیشوند
هشدار «Last background job execution ran...» در Nextcloud Docker اغلب از اجرا نشدن cron.php داخل کانتینر است. مسیر، کاربر و حالت cron را درست میکنیم.

رفع خطای no SASL authentication mechanisms در Postfix
وقتی smtpd پورت ۵۸۷ با خطای «no SASL authentication mechanisms» بالا نمیآید، ریشه معمولاً سوکت Dovecot است که داخل chroot پستفیکس ساخته نشده. این مقاله علت و راهحل کامل را قدمبهقدم بررسی میکند.

رفع dkim=none در Postfix با راهاندازی OpenDKIM
اگر ایمیلهای سرور Postfix شما در Gmail به اسپم میروند و هدر پیام dkim=none نشان میدهد، یعنی هیچ امضای DKIM روی پیامها درج نمیشود. در این مقاله OpenDKIM را نصب، پیکربندی و به Postfix وصل میکنیم و رکورد DNS را منتشر میکنیم.
در پیادهسازی به کمک نیاز دارید؟
تیم کلودیکپ همین کار را هر روز برای تیمهای دیگر انجام میدهد. اگر جایی گیر کردهاید، با ما صحبت کنید.