SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-21

راه اندازی سرور ایمیل تست با Mailpit روی VPS

با استفاده از Mailpit و Docker Compose یک سرور SMTP برای تست ایمیل بسازید. این راهنما نحوه جلوگیری از ارسال ایمیل‌های تست به کاربران واقعی و مشاهده پیام‌ها در وب را آموزش می‌دهد.

صندوق ایمیل یک‌بارمصرف چیست

یک صندوق ایمیل یک‌بارمصرف، یک سرور کوچک SMTP (پروتکل ساده انتقال نامه) است که ایمیل‌ها را برای هر آدرسی می‌پذیرد اما هیچ‌کدام را ارسال نمی‌کند. برنامه محیط staging شما به جای استفاده از یک سرویس‌دهنده ایمیل واقعی، ایمیل‌ها را به این سرور می‌فرستد و تمام پیام‌ها در همان‌جا متوقف می‌شوند. شما پیام‌های دریافتی را در یک رابط وب مشاهده می‌کنید؛ بنابراین، اشتباه در لیست گیرندگان یا خرابی قالب ایمیل هزینه‌ای برای شما ندارد، زیرا ایمیل هرگز از این صندوق خارج نمی‌شود.

این راهنما یک نمونه از آن را روی یک VPS با استفاده از Docker Compose پیاده‌سازی می‌کند. Mailpit نقش دریافت‌کننده نهایی (catch-all sink) را ایفا می‌کند. شنونده SMTP آن در جایی محدود شده است که فقط برنامه شما به آن دسترسی دارد، رابط وب آن پشت nginx با امنیت لایه انتقال (TLS) و رمز عبور قرار گرفته است و یک محدودیت نگهداری (retention limit) از پر شدن دیسک توسط صندوق ایمیل جلوگیری می‌کند. اگر با Compose آشنا نیستید، اصول اولیه Compose برای VPS ساختار فایلی که این راهنما بر اساس آن فرض شده است را پوشش می‌دهد.

نتیجه نهایی یک ابزار تست است، نه یک سرور ایمیل. این ابزار فاقد حساب کاربری، قابلیت ارسال و فیلتر اسپم است. صندوق‌های ایمیل واقعی برای کاربران واقعی، یک سرور ایمیل کامل مانند Mailcow هستند که پیاده‌سازی آن‌ها وظیفه‌ای بسیار بزرگ‌تر است.

مقایسه Mailpit، Inbucket و MailHog: کدام sink را اجرا کنیم

سه ابزار این وظیفه را انجام می‌دهند. تفاوت آن‌ها در وضعیت نگهداری، پورت‌هایی که روی آن‌ها گوش می‌دهند و کارهایی است که پس از دریافت پیام انجام می‌دهند. نسخه‌های زیر در اوت 2026 بررسی شده‌اند.

MailHog (mailhog/mailhog) روی پورت 1025 برای SMTP گوش می‌دهد و رابط کاربری خود را روی 8025 ارائه می‌کند. این ابزار همچنان کار می‌کند. شاخه پیش‌فرض آن از اوت 2022 هیچ commit جدیدی نداشته و سیستم ردیابی باگ‌های آن بیش از 250 مورد باز دارد؛ بنابراین شما در مسیر تست خود از وابستگی‌های وصله‌نشده استفاده خواهید کرد. پروژه جدیدی را با آن شروع نکنید.

Inbucket (inbucket/inbucket) روی پورت 2500 برای SMTP، پورت 9000 برای رابط وب و پورت 1100 برای POP3 (نسخه 3 پروتکل اداره پست) گوش می‌دهد. نسخه 3.1.1 در دسامبر 2025 منتشر شد. این ابزار پیام‌ها را به عنوان فایل در مسیر /storage ذخیره کرده و به‌طور خودکار آن‌ها را پاکسازی می‌کند: image مربوطه متغیرهای INBUCKET_STORAGE_RETENTIONPERIOD=72h و INBUCKET_STORAGE_MAILBOXMSGCAP=300 را تنظیم می‌کند. زمانی آن را انتخاب کنید که تست شما نیاز دارد ایمیل‌ها را به جای فراخوانی HTTP، با یک کتابخانه کلاینت POP3 جمع‌آوری کند.

Mailpit (axllent/mailpit) از همان پورت‌های MailHog یعنی 1025 و 8025 استفاده می‌کند، بنابراین بدون نیاز به تغییر در پیکربندی برنامه، جایگزین MailHog می‌شود. نسخه 1.30.7 در 8 اوت 2026 منتشر شد. این ابزار آنچه این راهنما نیاز دارد را درون فایل اجرایی خود جای داده است: یک فایل رمز عبور برای رابط وب و API (رابط برنامه‌نویسی کاربردی)، محدودیت تعداد پیام، محدودیت زمانی پیام و فیلتر گیرنده. ادامه این راهنما از Mailpit استفاده می‌کند.

نحوه عملکرد catch-all و دلیل عدم دخالت DNS

برنامه شما برای تحویل پیام در اینجا هیچ جستجویی انجام نمی‌دهد. شما یک host و یک port به آن می‌دهید، برنامه یک اتصال TCP برقرار می‌کند و RCPT TO:<anyone@example.test> را اعلام می‌نماید. Mailpit آن گیرنده را فارغ از محتوایش می‌پذیرد، پیام را ذخیره می‌کند و هیچ چیزی را فوروارد نمی‌کند. دامنه هرگز resolve نمی‌شود، بنابراین example.test کار می‌کند، حتی با اینکه .test یک نام رزرو شده است که در هیچ کجای سیستم نام دامنه (DNS) وجود ندارد.

این کل مکانیزم است و به همین دلیل است که صندوق ورودی به‌صورت پیش‌فرض امن است. هیچ رکورد MX (تبادل‌گر ایمیل) در کار نیست، هیچ تلاشی برای تحویل انجام نمی‌شود و هیچ پیامی نمی‌تواند به یک شخص واقعی برسد.

هدایت اپلیکیشن استیجینگ به سمت sink

هنگامی که اپلیکیشن به صورت کانتینر در همان پروژه Compose اجرا می‌شود، میزبان SMTP آن را روی mailpit تنظیم کنید؛ در صورتی که روی میزبان (host) اجرا می‌شود، آن را روی 127.0.0.1 قرار دهید. پورت را روی 1025 تنظیم کنید، TLS را غیرفعال نمایید و نام کاربری و رمز عبور را خالی بگذارید. Mailpit ایمیل‌های ناشناس را می‌پذیرد.

برخی فریم‌ورک‌ها بدون اعتبارنامه از ارسال ایمیل خودداری می‌کنند. MP_SMTP_AUTH_ACCEPT_ANY=1 باعث می‌شود Mailpit هر نام کاربری و رمز عبوری را بپذیرد و MP_SMTP_AUTH_ALLOW_INSECURE=1 مکانیزم‌های PLAIN و LOGIN را روی اتصال رمزنگاری‌نشده مجاز می‌شمارد. این دو تنظیم تنها به این دلیل در اینجا امن هستند که listener از طریق اینترنت در دسترس نیست؛ موضوعی که در استقرار زیر اعمال شده است.

تنظیم MP_SMTP_ALLOWED_RECIPIENTS از همان روز اول توصیه می‌شود. این پارامتر یک عبارت منظم (regular expression) می‌گیرد و تمام گیرندگانی که با آن مطابقت ندارند را رد می‌کند. آن را به سمت دامنه تست خود هدایت کنید؛ در این صورت، اگر دیتابیس استیجینگ همچنان حاوی آدرس واقعی یک مشتری باشد، به جای پیامی که بی‌سروصدا در sink می‌نشیند، یک خطای قابل مشاهده در لاگ اپلیکیشن شما ایجاد خواهد شد.

فایل Docker Compose

ابتدا دایرکتوری و یک فایل رمز عبور برای رابط وب ایجاد کنید. htpasswd -B یک هش bcrypt می‌نویسد و Mailpit هم bcrypt و هم متن ساده را می‌خواند.

mkdir -p ~/mailpit/data
cd ~/mailpit
sudo apt update && sudo apt install -y apache2-utils
htpasswd -B -c data/ui-auth qa

فایل compose.yaml را بنویسید:

services:
  mailpit:
    image: axllent/mailpit:v1.30
    container_name: mailpit
    restart: unless-stopped
    ports:
      - "127.0.0.1:8025:8025"
      - "127.0.0.1:1025:1025"
    volumes:
      - ./data:/data
    environment:
      MP_DATABASE: /data/mailpit.db
      MP_MAX_MESSAGES: 2000
      MP_MAX_AGE: 14d
      MP_UI_AUTH_FILE: /data/ui-auth
      MP_SMTP_AUTH_ACCEPT_ANY: 1
      MP_SMTP_AUTH_ALLOW_INSECURE: 1
      MP_SMTP_ALLOWED_RECIPIENTS: '@example\.test$$'

علامت دلار دوتایی اشتباه تایپی نیست. Compose یک $ تکی را به عنوان شروع متغیری برای بسط‌دادن می‌خواند، بنابراین $$ روشی است که با آن یک علامت دلار واقعی را به داخل کانتینر می‌فرستید. عبارت منظم (regex) به صورت @example\.test$ به Mailpit می‌رسد.

آن را اجرا کرده و وضعیت سلامت را بررسی کنید:

docker compose up -d
docker compose ps

ستون STATUS باید Up ... (healthy) را نشان دهد. این ایمیج دارای یک healthcheck داخلی است که هر 15 ثانیه /mailpit readyz را اجرا می‌کند، بنابراین کانتینری که در وضعیت starting باقی می‌ماند یا به unhealthy تغییر وضعیت می‌دهد، در داخل کانتینر روی پورت 8025 سرویس‌دهی نمی‌کند. پیش از تغییر هر چیز دیگری، docker compose logs mailpit را مطالعه کنید.

هر دو پورت منتشرشده دارای یک آدرس هستند و آن آدرس، کنترل امنیتی محسوب می‌شود. در داخل کانتینر، Mailpit روی 0.0.0.0 گوش می‌دهد که مشکلی ندارد، زیرا کانتینر فضای نام شبکه (network namespace) اختصاصی خود را دارد. سمت چپ نگاشت (mapping) تعیین می‌کند چه کسی از بیرون به آن دسترسی داشته باشد. اگر 8025:8025 را بنویسید، Docker آن را به تمام آدرس‌های روی میزبان، از جمله آدرس عمومی، متصل (bind) می‌کند.

اگر اپلیکیشن staging شما سرویسی در همین فایل است، نگاشت 1025 را به‌طور کامل حذف کنید و اپلیکیشن را به نام میزبان mailpit روی پورت 1025 هدایت کنید. کانتینرها در یک شبکه مشترک Compose مستقیماً به یکدیگر دسترسی دارند، بنابراین پورت SMTP هرگز با میزبان تماس پیدا نمی‌کند. نحوه حل نام سرویس‌ها در شبکه‌های Compose این جستجو را پوشش می‌دهد.

ارسال یک پیام و بررسی دریافت آن

python3 - <<'EOF'
import smtplib
from email.message import EmailMessage

m = EmailMessage()
m["From"] = "staging@example.test"
m["To"] = "anyone@example.test"
m["Subject"] = "Mailpit smoke test"
m.set_content("If this appears in the web interface, the sink works.")
with smtplib.SMTP("127.0.0.1", 1025) as s:
    s.send_message(m)
EOF

این اسکریپت در صورت موفقیت، خروجی خاصی نمایش نمی‌دهد. ذخیره شدن پیام را از طریق API بررسی کنید:

curl -s -u qa:yourpassword http://127.0.0.1:8025/api/v1/messages

این دستور یک JSON شامل لیست پیام‌های ذخیره‌شده برمی‌گرداند. اگر فلگ -u را حذف کنید، درخواست رد می‌شود؛ زیرا MP_UI_AUTH_FILE همزمان از API و رابط وب محافظت می‌کند. هر تستی که قصد خواندن صندوق ورودی را دارد، باید این اعتبارنامه‌ها را نیز ارسال کند.

خطای ConnectionRefusedError در اسکریپت Python به این معناست که هیچ سرویسی روی 127.0.0.1:1025 گوش نمی‌دهد. اگر نگاشت SMTP را حذف کرده باشید، این نتیجه مورد انتظار است و در آن صورت، بررسی باید از داخل یک container در همان شبکه Compose اجرا شود.

انتشار رابط وب از طریق nginx با رمز عبور

این رابط در حال حاضر فقط روی آدرس loopback پاسخ می‌دهد. nginx عملیات TLS termination را انجام می‌دهد و پیش از آنکه هر درخواستی به سرویس برسد، درخواست رمز عبور می‌کند.

sudo htpasswd -B -c /etc/nginx/mailpit.htpasswd qa
server {
    listen 443 ssl;
    server_name mail-test.example.com;

    ssl_certificate     /etc/letsencrypt/live/mail-test.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mail-test.example.com/privkey.pem;

    auth_basic           "mailpit";
    auth_basic_user_file /etc/nginx/mailpit.htpasswd;

    location / {
        proxy_pass http://127.0.0.1:8025;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

پس از بررسی نحو (syntax) با دستور sudo nginx -t && sudo systemctl reload nginx، تنظیمات را reload کنید. اگر این اولین تجربه شما در راه‌اندازی proxy است، مطالعه عملکرد هر دستور در بلوک reverse proxy توصیه می‌شود.

در فایل nginx و در data/ui-auth از نام کاربری و رمز عبور یکسانی استفاده کنید. nginx هدر Authorization مرورگر را به سمت upstream ارسال می‌کند، بنابراین تطابق اعتبارنامه‌ها باعث می‌شود هر دو بررسی با یک بار وارد کردن رمز عبور تأیید شوند. استفاده از اعتبارنامه‌های متفاوت باعث می‌شود مرورگر مجموعه‌ای از اطلاعات را نگه دارد که بررسی دوم آن را رد می‌کند.

هدرهای Upgrade و Connection صرفاً تزئینی نیستند. Mailpit ایمیل‌های جدید را از طریق WebSocket به صفحه‌ای که باز است ارسال می‌کند و پروکسی که HTTP/1.1 را بدون این هدرها اجرا کند، نمی‌تواند اتصال را ارتقا (upgrade) دهد. در آن صورت صفحه به‌درستی بارگذاری می‌شود اما تغییر نمی‌کند: ایمیل می‌رسد، API آن را نشان می‌دهد، اما لیست تا زمانی که صفحه را reload نکنید، ثابت می‌ماند.

هر دو لایه امنیتی را حفظ کنید. رمز عبور nginx از آدرس عمومی محافظت می‌کند و MP_UI_AUTH_FILE از خود پورت 8025 محافظت می‌کند؛ این موضوع اهمیت دارد زیرا تمام لینک‌های بازنشانی رمز عبوری که برنامه staging شما تا به حال تولید کرده است، در آن رابط قابل خواندن هستند.

هرگز اجازه ندهید sink به یک open relay تبدیل شود

یک open relay سرور SMTP است که پیام را از هر کسی دریافت کرده و به هر مقصدی ارسال می‌کند. اسپمرها دائماً به دنبال چنین سرورهایی هستند و یافتن یکی از آن‌ها روی آدرس شما، منجر به گزارش‌های سوءاستفاده و تعلیق حساب کاربری می‌شود.

Mailpit به‌صورت پیش‌فرض یک open relay نیست، زیرا هرگز پیام‌ها را فوروارد نمی‌کند. قابلیت relay تا زمانی که MP_SMTP_RELAY_CONFIG را به یک فایل پیکربندی relay اشاره ندهید، غیرفعال می‌ماند و دکمه release در رابط کاربری نیز تا آن زمان هیچ کاری انجام نمی‌دهد. تنظیم‌نکردن آن یک انتخاب آگاهانه است.

دو راه برای از دست دادن این ویژگی وجود دارد. اگر یک relay پیکربندی کنید تا دکمه release کار کند و سپس پورت SMTP را در معرض اینترنت قرار دهید، یک open relay فعال ساخته‌اید. اگر پورت را بدون relay در معرض قرار دهید، غریبه‌ها نمی‌توانند از طریق شما ایمیل ارسال کنند، اما می‌توانند فضای ذخیره‌سازی شما را پر کرده و محتوایی را وارد رابط کاربری کنند که تیم شما به آن اعتماد دارد.

دام اصلی در یک میزبان Docker، فایروال است. انتشار (Publish) یک پورت باعث می‌شود Docker قوانین خود را در جدول nat بنویسد و ترافیک مقصدِ کانتینر، پیش از آنکه قوانین ufw (فایروال ساده) اعمال شوند، در آنجا مطابقت داده می‌شود. sudo ufw deny 1025/tcp گزارش موفقیت می‌دهد اما چیزی را تغییر نمی‌دهد. چرا Docker پورت‌ها را مستقیماً و بدون توجه به ufw منتشر می‌کند ترتیب زنجیره‌ها را بررسی می‌کند.

راه حل، آدرس در mapping است، نه یک قانون فایروال. بررسی کنید چه چیزی واقعاً bind شده است:

sudo ss -ltnp | grep -E ':(1025|8025)'

خروجی سالم، 127.0.0.1:1025 و 127.0.0.1:8025 را نشان می‌دهد. خطی که 0.0.0.0:1025 را نمایش می‌دهد به این معنی است که mapping آدرس خود را از دست داده و sink در حال گوش‌دادن به اینترنت است. از یک ماشین دیگر، nc -vz mail-test.example.com 1025 باید با timeout مواجه شود یا رد (refuse) شود.

هنگامی که برنامه روی سرور دیگری قرار دارد، پورت 1025 را برای اتصال این دو باز نکنید. هر دو ماشین را در یک شبکه خصوصی یا تونل VPN قرار دهید و mapping را به آدرس آن اینترفیس bind کنید.

فقط در صورتی که واقعاً به ایمیل ورودی نیاز دارید، رکوردهای MX را منتشر کنید

یک رکورد MX (تبادل‌گر ایمیل) به سایر سرورهای ایمیل می‌گوید که کدام میزبان، ایمیل‌های مربوط به یک دامنه را می‌پذیرد. بدون وجود رکورد MX برای دامنهٔ موقت شما، هیچ ایمیلی از اینترنت دریافت نخواهد شد، زیرا سرورهای فرستنده مقصدی برای تحویل آن ندارند. صندوق ورودی فقط شامل مواردی است که برنامه‌های خودتان ارسال کرده‌اند، که این دقیقاً هدف یک صندوق پستی آزمایشی است.

دریافت ایمیل واقعی به معنای داشتن یک رکورد MX است که به سرور شما اشاره می‌کند، Mailpit که روی پورت 25 (MP_SMTP_BIND_ADDR=0.0.0.0:25) گوش می‌دهد، و باز بودن آن پورت. در آن لحظه، شما در حال اجرای یک سرویس عمومی catch-all برای تمام آدرس‌های آن دامنه هستید. نسبت به پیامدهای زیر آگاه باشید:

  • اسپم‌ها چند روز پس از ظاهر شدن رکورد شروع می‌شوند، زیرا ابزارهای جمع‌آوری اطلاعات، DNS را اسکن می‌کنند. حملات دیکشنری سپس نام‌های رایج را امتحان کرده و برای هر تلاش، یک پیام ذخیره می‌کنند.
  • پیوست‌های ارسالی از سوی غریبه‌ها روی دیسک شما می‌نشینند و همان‌جا باقی می‌مانند. هیچ فیلتری برای آن‌ها وجود ندارد، بنابراین آرشیوی از فرستندگان ناشناس در کنار ایمیل‌های آزمایشی خودتان قرار می‌گیرد.
  • هر کسی که دامنه را بشناسد می‌تواند با استفاده از یک آدرس در آن دامنه در سرویس‌های شخص ثالث ثبت‌نام کند و ایمیل تأیید به سرور شما تحویل داده می‌شود. اگر سد امنیتی رمز عبور روزی نفوذپذیر شود، آن حساب‌ها متعلق به کسی خواهد بود که صندوق ورودی را می‌خواند.
  • محدودیت‌های نگهداری (Retention) دیگر یک کار نظافتی ساده نیستند و به یک بارِ کاری سنگین تبدیل می‌شوند، زیرا حجم ایمیل‌ها دیگر تحت کنترل شما نیست.

اگر برای بررسی قابلیت تحویل (deliverability) به ایمیل ورودی واقعی نیاز دارید، یک زیردامنه اختصاصی برای آن در نظر بگیرید، MP_MAX_AGE را کوتاه نگه دارید و با تمام محتویات آن به عنوان داده‌های عمومی رفتار کنید. اگر به صندوق‌های پستی نیاز دارید که افراد به آن‌ها تکیه می‌کنند، به جای آن از یک سرور ایمیل واقعی با قابلیت‌های فیلترینگ و پشتیبان‌گیری استفاده کنید.

نگهداری: چگونه یک catch-all نامحدود دیسک را پر می‌کند

نرم‌افزار Mailpit به‌صورت پیش‌فرض 500 پیام را نگه می‌دارد و به‌طور دوره‌ای قدیمی‌ترین پیام‌های مازاد بر این تعداد را حذف می‌کند. استفاده از MP_MAX_MESSAGES: 0 حذف خودکار را به‌طور کامل غیرفعال می‌کند و همین تغییر ساده باعث می‌شود یک catch-all بدون آنکه کسی متوجه شود، دیسک را پر کند. MP_MAX_AGE یک محدودیت زمانی اضافه می‌کند و بازه‌های زمانی را به ساعت یا روز، به‌صورت 36h یا 14d دریافت می‌کند.

MP_DATABASE تعیین می‌کند که آیا داده‌ها پس از خروج از برنامه باقی می‌مانند یا خیر. بدون این گزینه، Mailpit در یک فایل موقت می‌نویسد که با خروج پردازش حذف می‌شود؛ بنابراین هر بار راه‌اندازی مجدد (restart)، صندوق ورودی را خالی می‌کند. با فعال‌سازی این گزینه، ایمیل‌ها پس از راه‌اندازی مجدد باقی می‌مانند و حجم فایل افزایش می‌یابد.

فایل‌های پیوست عامل اصلی اشغال فضا هستند. یک وظیفه شبانه که یک گزارش PDF با حجم 2 مگابایت را به 300 آدرس تست ارسال می‌کند، در هر شب 600 مگابایت فضا اشغال می‌کند و محدودیت تعداد پیام به‌تنهایی نمی‌تواند به‌موقع واکنش نشان دهد. این رشد حجم را در کنار سایر سرویس‌هایی که از فضای ذخیره‌سازی مشترک استفاده می‌کنند در نظر بگیرید، زیرا همسایه‌های پرمصرفی مانند PhotoPrism یا Immich احتمالاً بخش بزرگی از دیسک یک VPS کوچک را از قبل اشغال کرده‌اند.

du -h ~/mailpit/data/mailpit.db
df -h /

به‌جای انتظار برای رسیدن به سقف مجاز، فروشگاه داده را بین اجرای تست‌های CI خالی کنید:

curl -s -u qa:yourpassword -X DELETE http://127.0.0.1:8025/api/v1/messages

نرم‌افزار Inbucket همین مشکل را با استفاده از INBUCKET_STORAGE_RETENTIONPERIOD (72 ساعت در ایمیج پیش‌فرض) و INBUCKET_STORAGE_MAILBOXMSGCAP (300 پیام) مدیریت می‌کند. از هر کدام که استفاده می‌کنید، پیش از آنکه اولین مجموعه تست (test suite) را به سمت آن هدایت کنید، محدودیت‌ها را تعیین نمایید.

خواندن صندوق ورودی از مجموعه تست

GET /api/v1/messages آنچه ذخیره شده است را فهرست می‌کند، GET /api/v1/message/{ID} یک پیام را به همراه بخش‌ها و هدرهای آن بازمی‌گرداند، GET /api/v1/search فیلتر می‌کند و DELETE /api/v1/messages فضای ذخیره‌سازی را پاکسازی می‌کند. مستندات تعاملی برای نسخه‌ای که در حال اجرای آن هستید در http://127.0.0.1:8025/api/v1/ ارائه می‌شود.

یک تست مفید، پیامی ارسال می‌کند، تا زمان ظاهر شدن آن نظرسنجی (poll) می‌کند، موضوع و لینک درون آن را بررسی کرده و سپس همه چیز را حذف می‌کند. به جای استفاده از یک درخواست واحد، از یک حلقه تکرار کوتاه برای نظرسنجی استفاده کنید؛ زیرا برنامه‌ای که ایمیل‌ها را در یک worker پس‌زمینه صف‌بندی می‌کند، پیش از آنکه Mailpit پیام را دریافت کند، از فراخوانی ارسال (send call) بازمی‌گردد. همین الگو در ابزارهای تست و mocking برای APIهای self-hosted دیده می‌شود که معمولاً نیمه دیگر محیط staging هستند و هرگز با محیط production در تماس نیستند.

FAQ

آیا یک صندوق ایمیل یک‌بارمصرف که خودمان میزبانی می‌کنیم، یک open relay است؟

خیر، تا زمانی که قابلیت relay غیرفعال باشد. Mailpit پیام‌ها را ذخیره می‌کند و هرگز آن‌ها را ارسال نمی‌کند، مگر اینکه شما MP_SMTP_RELAY_CONFIG را به یک پیکربندی relay اشاره دهید. بنابراین، فرد غریبه‌ای که به پورت 1025 دسترسی پیدا کند، نمی‌تواند از طریق سرور شما ایمیل بفرستد. با این حال، آن‌ها همچنان می‌توانند فضای ذخیره‌سازی شما را پر کنند؛ پس پورت SMTP را فقط به آدرسی bind کنید که برنامهٔ شما به آن دسترسی دارد. انتشار آن به صورت 1025:1025 در Compose باعث می‌شود تمام آدرس‌های میزبان bind شوند و sudo ufw deny 1025/tcp نیز آن را نمی‌بندد، زیرا قوانین NAT خودِ Docker اولویت دارند.

آیا برای دامنهٔ تست خود به رکورد MX نیاز دارم؟

فقط اگر می‌خواهید ایمیل‌های اینترنت به دست شما برسد. بدون رکورد MX، سرورهای فرستنده مقصدی برای تحویل ندارند، بنابراین صندوق ورودی فقط آنچه را که برنامه‌های خودتان از طریق SMTP ارسال می‌کنند، نگه می‌دارد. اگر رکورد را منتشر کنید و پورت 25 را باز بگذارید، در حال اجرای یک catch-all عمومی هستید: اسپم‌ها در عرض چند روز سرازیر می‌شوند، حملات dictionary به ازای هر تلاش یک پیام ذخیره می‌کنند و پیوست‌های افراد غریبه بدون هیچ فیلتری روی دیسک شما قرار می‌گیرند.

چرا لیست پیام‌ها فقط با رفرش کردن صفحه به‌روز می‌شود؟

Mailpit ایمیل‌های جدید را از طریق WebSocket به صفحه‌ای که باز است ارسال می‌کند. اگر در بلاک location مربوط به nginx، تنظیمات proxy_http_version 1.1 و هدرهای Upgrade و Connection وجود نداشته باشد، اتصال ارتقا نمی‌یابد و صفحه به‌صورت عادی بارگذاری شده و سپس ثابت می‌ماند. ایمیل‌ها همچنان می‌رسند و API آن‌ها را برمی‌گرداند، به همین دلیل صندوق ورودی قدیمی به نظر می‌رسد، نه خراب. آن خطوط را اضافه کنید، nginx را reload کنید و سپس صفحه را رفرش کنید.

چگونه از پر شدن دیسک توسط صندوق ورودی جلوگیری کنم؟

مقدار MP_MAX_MESSAGES را روی یک عدد واقعی نگه دارید و MP_MAX_AGE را اضافه کنید. سقف پیش‌فرض 500 پیام است و تنظیم آن روی 0 حذف پیام‌ها را به‌طور کامل غیرفعال می‌کند؛ این همان دلیلی است که باعث می‌شود یک catch-all با پیوست‌ها به‌آرامی رشد کند. MP_MAX_AGE مقادیر زمانی بر اساس ساعت یا روز را می‌پذیرد، مانند 36h یا 14d. در مرحلهٔ teardown در CI، با استفاده از curl -X DELETE http://127.0.0.1:8025/api/v1/messages فضای ذخیره‌سازی را پاک کنید. Inbucket نیز همین کار را با INBUCKET_STORAGE_RETENTIONPERIOD (72 ساعت) و INBUCKET_STORAGE_MAILBOXMSGCAP (300 پیام) انجام می‌دهد.

آیا باید از Mailpit، Inbucket یا MailHog استفاده کنم؟

از آگوست 2026، برای پروژه‌های جدید از Mailpit استفاده کنید. MailHog همچنان کار می‌کند، اما شاخهٔ پیش‌فرض آن از آگوست 2022 هیچ commit جدیدی نداشته است و وابستگی‌های آن بدون وصله (unpatched) باقی مانده‌اند. Inbucket به‌طور فعال نگهداری می‌شود (نسخه 3.1.1، دسامبر 2025) و اگر تست شما به POP3 نیاز دارد، انتخاب بهتری است؛ چرا که سرور POP3 در Mailpit تنها زمانی شروع به کار می‌کند که یک فایل رمز عبور به آن بدهید. Mailpit از همان پورت‌های MailHog یعنی 1025 و 8025 استفاده می‌کند، بنابراین جایگزینی MailHog تنها به تغییر نام image در فایل Compose شما نیاز دارد.