SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor

استضافة صندوق بريد مؤقت بـ Mailpit على VPS

التقط كل رسائل الاختبار على نطاق مؤقت باستخدام Mailpit، واقرأها من واجهة الويب، وامنع تطبيق staging من مراسلة العملاء الحقيقيين نهائياً.

ما هو صندوق البريد الإلكتروني المؤقت

صندوق البريد الإلكتروني المؤقت هو خادم SMTP (بروتوكول نقل البريد البسيط) صغير يقبل الرسائل لأي عنوان، لكنه لا يسلّم أياً منها. يرسل إليه تطبيق staging بدلاً من إرسالها إلى مزود بريد حقيقي، وتتوقف كل رسالة فيه. تقرأ الرسائل التي وصلت عبر واجهة ويب، لذلك لا تكلّفك قائمة مستلمين خاطئة أو قالب معطّل شيئاً، لأن الرسالة لا تغادر الصندوق.

يبني هذا الدليل صندوقاً واحداً على VPS باستخدام Docker Compose. يعمل Mailpit كمستودع شامل للرسائل. يستمع SMTP الخاص به على عنوان لا يستطيع الوصول إليه إلا تطبيقك، بينما توجد واجهة الويب خلف nginx مع أمان طبقة النقل (TLS) وكلمة مرور. ويمنع حد الاحتفاظ الرسائل من ملء القرص. إذا كان Compose جديداً عليك، يشرح دليل أساسيات Compose على VPS بنية الملفات التي يفترضها هذا الدليل.

النتيجة أداة اختبار وليست خادم بريد. لا تحتوي على حسابات، ولا تسليم للرسائل، ولا تصفية للرسائل المزعجة. أما صناديق البريد الحقيقية للمستخدمين الحقيقيين فتتطلب خادماً كاملاً للبريد مثل Mailcow، وهي مهمة أكبر بكثير.

Mailpit مقابل Inbucket مقابل MailHog: أي خدمة تجميع تختار

تؤدي ثلاث أدوات هذه الوظيفة. وتختلف من حيث حالة الصيانة، والمنافذ التي تستمع إليها، وما يمكنها فعله بالرسالة بعد قبولها. جرى التحقق من الإصدارات أدناه في October 2026.

MailHog (mailhog/mailhog) يستمع إلى SMTP على المنفذ 1025، ويعرض واجهته على المنفذ 8025. وما يزال يعمل. لم يشهد فرعه الافتراضي أي commit منذ August 2022، ويحتوي tracker على أكثر من 200 issue مفتوحة، لذلك ستشغّل dependencies غير مُرقّعة ضمن مسار الاختبار. لا تبدأ أعمالاً جديدة باستخدامه.

Inbucket (inbucket/inbucket) يستمع إلى SMTP على المنفذ 2500، وإلى واجهة الويب على المنفذ 9000، وإلى POP3 (بروتوكول مكتب البريد الإصدار 3) على المنفذ 1100. صدر الإصدار 3.2.0 في 10 October 2026. يخزّن الرسائل كملفات تحت /storage ويحذفها تلقائياً: يحدّد image القيمتين INBUCKET_STORAGE_RETENTIONPERIOD=72h وINBUCKET_STORAGE_MAILBOXMSGCAP=300. اختره عندما يحتاج الاختبار إلى جمع البريد باستخدام مكتبة عميل POP3 بدلاً من استدعاء HTTP.

Mailpit (axllent/mailpit) يستخدم المنافذ نفسها التي يستخدمها MailHog، وهما 1025 و8025، لذلك يمكنك استبدال MailHog من دون تعديل إعدادات التطبيق. صدر الإصدار 1.31.5 في 11 October 2026؛ وانتهى خط الإصدار 1.30 عند الإصدار 1.30.7 في August. يتضمن كل ما يحتاج إليه هذا الدليل داخل binary: ملف password لواجهة الويب وAPI (واجهة برمجة التطبيقات)، وحداً أقصى لعدد الرسائل، وحداً أقصى لعمر الرسائل، ومرشحاً للمستلمين. يستخدم باقي هذا الدليل Mailpit.

آلية catch-all، ولماذا لا علاقة لـDNS بالأمر

لا يبحث تطبيقك هنا عن وجهة التسليم. أنت تمرّر إليه مضيفاً ومنفذاً، فيفتح اتصال TCP ويعلن RCPT TO:<anyone@example.test>. يقبل Mailpit ذلك المستلم مهما كانت قيمته، ويخزّن الرسالة ولا يرسلها إلى أي جهة. لا يُجرى تحليل اسم النطاق مطلقاً، لذلك يعمل example.test رغم أن .test اسم محجوز لا وجود له في نظام أسماء النطاقات (DNS).

هذه هي الآلية كاملة، ولهذا تكون علبة الوارد آمنة افتراضياً. لا يُستخدم أي سجل MX (mail exchanger)، ولا تُجرى أي محاولة تسليم، ولا يمكن لأي رسالة أن تصل إلى شخص حقيقي.

وجّه تطبيق الاختبار إلى خادم الالتقاط

اضبط مضيف SMTP في التطبيق على mailpit عندما يعمل التطبيق كحاوية ضمن مشروع Compose نفسه، أو على 127.0.0.1 عندما يعمل على الخادم المضيف. اضبط المنفذ على 1025، وعطّل TLS، واترك اسم المستخدم وكلمة المرور فارغين. يقبل Mailpit الرسائل المرسلة دون مصادقة.

ترفض بعض أطر العمل الإرسال من دون بيانات اعتماد. يتيح MP_SMTP_AUTH_ACCEPT_ANY=1 لـMailpit قبول أي اسم مستخدم وكلمة مرور، ويسمح MP_SMTP_AUTH_ALLOW_INSECURE=1 باستخدام آليتي PLAIN وLOGIN عبر اتصال غير مشفّر. يكون هذان الإعدادان آمنين هنا فقط لأن المستمع غير قابل للوصول من الإنترنت، وهو ما يفرضه النشر أدناه.

يستحسن ضبط MP_SMTP_ALLOWED_RECIPIENTS منذ اليوم الأول. يتلقى هذا الإعداد تعبيراً نمطياً ويرفض كل مستلم لا يطابقه. وجّهه إلى نطاق الاختبار، وعند احتواء قاعدة بيانات الاختبار على عنوان حقيقي لأحد العملاء، سيظهر فشل واضح في سجل التطبيق بدلاً من وصول الرسالة بصمت إلى خادم الالتقاط.

ملف 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.31
    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 علامة $ واحدة باعتبارها بداية متغير يجب توسيعه، لذلك فإن $$ هي الطريقة لتمرير علامة دولار حرفية واحدة إلى الحاوية. يصل التعبير النمطي إلى Mailpit بالشكل @example\.test$.

شغّل الحاوية وتحقق من حالة الصحة:

docker compose up -d
docker compose ps

يجب أن يقرأ عمود STATUS القيمة Up ... (healthy). تتضمن الصورة healthcheck خاصاً بها، ويشغّل /mailpit readyz كل 15 ثانية. لذلك، فإن بقاء الحاوية على الحالة starting أو تحوّلها إلى unhealthy يعني عادةً أن Mailpit لا يخدم الطلبات على المنفذ 8025 داخل الحاوية. يلتقط الفحص الإعدادات من متغيرات البيئة MP_، لكنه لا يلتقطها من أعلام سطر الأوامر. وهذا أحد أسباب وضع هذا الدليل كل الإعدادات ضمن environment:. إذا مرّرت الخيارات كأعلام، فقد يعرض الفحص الحالة unhealthy بينما يعمل Mailpit بصورة طبيعية. اقرأ docker compose logs mailpit قبل تغيير أي شيء آخر.

يحتوي كل منفذ منشور على عنوان، وهذا العنوان هو عنصر التحكم الأمني. يستمع Mailpit داخل الحاوية على 0.0.0.0، وهذا مناسب لأن للحاوية مساحة أسماء شبكة مستقلة. يحدد الجانب الأيسر من الربط من يمكنه الوصول إلى المنفذ من الخارج. إذا كتبت 8025:8025، فسيعمل Docker على ربط كل عنوان على الخادم، بما في ذلك العنوان العام.

إذا كان تطبيق 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، وعندها يجب تشغيل الاختبار من حاوية على شبكة Compose نفسها.

نشر واجهة الويب عبر nginx باستخدام كلمة مرور

تستجيب الواجهة حالياً على عنوان loopback فقط. ينهي nginx اتصال TLS ويطلب كلمة مرور قبل وصول أي طلب إليها.

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;
    }
}

أعد التحميل بعد فحص بناء الجملة باستخدام sudo nginx -t && sudo systemctl reload nginx. يُستحسن قراءة وظيفة كل توجيه في كتلة reverse proxy مرة واحدة إذا كانت هذه أول مرة تستخدم فيها proxy.

استخدم اسم المستخدم وكلمة المرور نفسيهما في ملف nginx وفي data/ui-auth. يمرّر nginx ترويسة Authorization التي يرسلها المتصفح إلى الخدمة الخلفية، ولذلك تتحقق بيانات الاعتماد المتطابقة من الشرطين عبر مطالبة واحدة. أما بيانات الاعتماد المختلفة فتترك في المتصفح مجموعة واحدة ترفضها عملية التحقق الثانية.

ترويتا Upgrade وConnection ليستا للزينة. يرسل Mailpit البريد الجديد إلى صفحة مفتوحة عبر WebSocket، ولا يستطيع proxy يعمل باستخدام HTTP/1.1 من دون هاتين الترويتين ترقية الاتصال. بعد ذلك تُحمّل الصفحة بشكل صحيح، لكنها لا تتغير: يصل البريد، وتعرضه API، وتبقى القائمة ثابتة حتى تعيد تحميل الصفحة.

أبقِ آليتي الحماية معاً. تحمي كلمة مرور nginx العنوان العام، بينما يحمي MP_UI_AUTH_FILE المنفذ 8025 نفسه. وهذا مهم لأن كل رابط لإعادة تعيين كلمة المرور أنشأه تطبيق staging يظل قابلاً للقراءة في تلك الواجهة طوال مدة الاحتفاظ به.

لا تسمح أبداً بأن يصبح sink مرحِّلاً مفتوحاً

المرحل المفتوح هو خادم SMTP يستقبل رسالة من أي جهة ويمررها إلى أي وجهة. يفحصه مرسلو الرسائل المزعجة باستمرار، والعثور على واحد في عنوانك يؤدي إلى بلاغات إساءة استخدام وتعليق الحساب.

لا يُعد Mailpit مرحِّلاً مفتوحاً تلقائياً، لأنه لا يرسل أي شيء إلى الخارج افتراضياً. يوفّر ميزتين للإرسال إلى الخارج، وتبقى كلتاهما معطلتين إلى أن تهيئهما. يشير MP_SMTP_RELAY_CONFIG إلى ملف إعدادات المرحّل ويمكّن إجراء release في الواجهة، ما يرسل الرسالة التي تختارها عبر ذلك الخادم؛ أما الترحيل التلقائي فيحتاج إلى مفتاح إضافي، وهو MP_SMTP_RELAY_ALL (وقيمته الافتراضية false) أو MP_SMTP_RELAY_MATCHING. أما إعادة التوجيه، التي تضبطها عبر MP_SMTP_FORWARD_CONFIG أو متغيرات MP_SMTP_FORWARD_*، فتنشئ نسخة من كل رسالة واردة وترسلها إلى عناوين ثابتة تختارها. إن تركت كل هذه الإعدادات غير محددة فهو خيار مقصود.

هناك طريقتان لفقدان هذه الخاصية. فعّل MP_SMTP_RELAY_ALL، الذي يرسل كل رسالة واردة إلى مستلميها الأصليين ويتجاهل MP_SMTP_ALLOWED_RECIPIENTS، ثم عرّض منفذ SMTP للإنترنت، وستكون قد أنشأت مرحّلاً مفتوحاً يعمل فعلياً. وينفذ MP_SMTP_RELAY_MATCHING الأمر نفسه مع أي مستلم يطابق النمط المحدد. إذا عرّضت المنفذ من دون تفعيل الترحيل، فلن يتمكن الغرباء من إرسال البريد عبرك، لكنهم يستطيعون ملء مساحة التخزين وإدخال محتوى إلى الواجهة التي يثق بها فريقك. وعند تفعيل إعادة التوجيه، ستصل رسائلهم أيضاً إلى أي صندوق وارد يستقبل النسخ.

المشكلة على مضيف Docker هي الجدار الناري. يؤدي نشر منفذ إلى جعل Docker يكتب قواعده الخاصة في جدول nat، وتتم مطابقة حركة المرور المتجهة إلى الحاوية هناك قبل أن تتاح لقواعد ufw (uncomplicated firewall) فرصة معالجتها. يُبلغ sudo ufw deny 1025/tcp عن نجاح العملية من دون أن يغيّر شيئاً. يشرح سبب نشر Docker للمنافذ متجاوزاً ufw ترتيب السلسلة.

الإصلاح هو العنوان الموجود في الربط، وليس قاعدة في الجدار الناري. تحقّق مما هو مرتبط فعلياً:

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

يُظهر الخرج السليم 127.0.0.1:1025 و127.0.0.1:8025. ويعني السطر الذي يقرأ 0.0.0.0:1025 أن الربط فقد عنوانه، وأن sink يستمع للإنترنت. من جهاز آخر، ينبغي أن تنتهي مهلة nc -vz mail-test.example.com 1025 أو أن يُرفض الاتصال.

عندما يكون التطبيق على خادم مختلف، لا تفتح 1025 لربط الخادمين. ضع الجهازين على شبكة خاصة أو داخل نفق VPN، واربط الربط بعنوان واجهة تلك الشبكة.

انشر سجلات MX فقط إذا كنت تريد استقبال بريد وارد فعلي

يُخبر سجل MX (mail exchanger) خوادم البريد الأخرى بالمضيف الذي يستقبل البريد لنطاق معيّن. لا يؤدي غياب هذا السجل وحده إلى إغلاق الاستقبال: عندما لا يحتوي النطاق على سجل MX، تعود خوادم الإرسال إلى سجل العنوان الخاص به (A أو AAAA)، وفقاً لما يحدده القسم 5.1 من RFC 5321، واسم المضيف الذي توجّه إليه هذا الخادم لواجهة الويب لديه سجل عنوان. ما يمنع وصول بريد الإنترنت هو عدم وجود أي خدمة تستمع على المنفذ 25 في هذا الخادم. ولا يحتوي صندوق الوارد إلا على الرسائل التي أرسلتها تطبيقاتك بنفسها، وهذا هو الغرض من صندوق اختبار البريد.

يعني استقبال بريد فعلي وجود سجل DNS يوجّه خوادم الإرسال إلى هذا الخادم (سجل MX أو سجل العنوان الذي تعود إليه تلك الخوادم)، وأن تكون Mailpit تستمع على المنفذ 25 (MP_SMTP_BIND_ADDR=0.0.0.0:25)، وأن يكون هذا المنفذ مفتوحاً. عندها ستشغّل نقطة استقبال عامة لكل عنوان في النطاق. كن واضحاً بشأن النتائج المترتبة على ذلك.

  • يبدأ البريد العشوائي خلال أيام من ظهور السجل، لأن أدوات جمع العناوين تقرأ DNS. ثم تنفّذ هجمات القاموس محاولةً استخدام أسماء شائعة، وتحفظ رسالة لكل محاولة.
  • تصل المرفقات من مرسلين مجهولين إلى القرص وتبقى عليه. لا توجد أي آلية لتصفيتها، لذلك قد يبقى أرشيف من مرسل مجهول بجانب رسائل الاختبار الخاصة بك.
  • يستطيع أي شخص يعرف النطاق التسجيل في خدمات تابعة لجهات خارجية باستخدام عنوان ضمنه، وستُسلَّم رسالة التأكيد إلى خادمك. وإذا تمكّن أحد من تجاوز بوابة كلمة المرور، فستصبح تلك الحسابات ملكاً لمن يقرأ صندوق الوارد.
  • تتوقف حدود الاحتفاظ عن كونها إجراءً تنظيمياً وتصبح ضرورية لتحمّل الحمل، لأنك لم تعد تتحكم في حجم الرسائل.

إذا كنت تحتاج إلى بريد وارد فعلي لاختبار قابلية التسليم، فخصّص له نطاقاً فرعياً، واجعل MP_MAX_AGE قصيراً، وتعامل مع كل ما يرد إليه على أنه عام. وإذا كنت تحتاج إلى صناديق بريد يعتمد عليها المستخدمون، فشغّل خادم بريد فعلياً مزوّداً بالتصفية والنسخ الاحتياطية بدلاً من ذلك.

الاحتفاظ: كيف يملأ catch-all غير المقيّد القرص

يحتفظ Mailpit بـ500 رسالة افتراضياً، ويحذف دورياً الرسائل الأقدم التي تتجاوز هذا العدد. يعطّل MP_MAX_MESSAGES: 0 هذا الحد، ومع عدم تعيين MP_MAX_AGE لا تُحذف أي رسالة تلقائياً. بهذه الطريقة يمكن لـcatch-all أن يملأ القرص دون أن يلاحظ أحد. يضيف MP_MAX_AGE حداً زمنياً، ويمكن استخدامه مع حد عدد الرسائل أو بدلاً منه. وتُحدَّد مدته بالساعات أو الأيام، وتُكتب بالشكل 36h أو 14d.

يحدّد MP_DATABASE ما إذا كانت هذه البيانات ستبقى. بدونه، يكتب Mailpit إلى ملف مؤقت يُحذف عند خروج العملية، لذلك يؤدي كل restart إلى إفراغ صندوق الوارد. ومعه، تبقى الرسائل بعد restarts ويستمر الملف في النمو.

المرفقات هي التي تستهلك المساحة. إذا أرسلت مهمة ليلية تقرير PDF حجمه 2 MB إلى 300 عنوان اختبار، فسيُضاف 600 MB كل ليلة، ولن يتصرف حد عدد الرسائل وحده بالسرعة الكافية. احسب هذا النمو ضمن المساحة التي تشاركها خدمات أخرى، لأن خدمة مجاورة تستهلك الوسائط مثل 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 ‏(72h في image) و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/.

يرسل اختبار مفيد رسالة، ثم يستعلم بشكل متكرر حتى تظهر، ويتحقق من الموضوع والرابط الموجود فيها، ثم يحذف كل شيء. استخدم حلقة إعادة محاولة قصيرة للاستعلام بدلاً من إرسال طلب واحد، لأن التطبيق الذي يضع البريد في قائمة انتظار داخل عامل يعمل في الخلفية يعيد التحكم من استدعاء الإرسال قبل أن تصل الرسالة إلى Mailpit. يظهر النمط نفسه في أدوات اختبار ومحاكاة واجهات API المستضافة ذاتياً، وهي عادةً النصف الآخر من بيئة التجهيز التي لا تتصل ببيئة الإنتاج.

FAQ

هل يُعد صندوق البريد الإلكتروني المؤقت المستضاف ذاتياً مرحِّلاً مفتوحاً؟

ليس ما دام الترحيل معطّلاً. لا يرسل Mailpit أي رسائل إلى الخارج افتراضياً: يتيح MP_SMTP_RELAY_CONFIG فقط الإفراج اليدوي عن الرسائل التي تختارها، بينما يتطلب الترحيل التلقائي MP_SMTP_RELAY_ALL أو MP_SMTP_RELAY_MATCHING أيضاً. اترك هذه الإعدادات غير محددة، ولن يتمكن شخص غريب يصل إلى المنفذ 1025 من إرسال البريد عبر خادمك. لكنه سيظل قادراً على ملء مساحة التخزين لديك، لذلك اربط منفذ SMTP بعنوان لا يمكن لتطبيقك وحده الوصول إليه. يؤدي نشره بصيغة 1025:1025 في Compose إلى ربطه بكل عناوين المضيف، ولن يغلقه sudo ufw deny 1025/tcp لأن قواعد nat الخاصة بـDocker تُطابق أولاً.

هل أحتاج إلى سجل MX لنطاق الاختبار؟

فقط إذا أردت وصول البريد من الإنترنت، وغياب هذا السجل ليس حاجزاً بحد ذاته. عند عدم وجود سجل MX، تعود خوادم الإرسال إلى سجل A أو AAAA الخاص بالنطاق، لذلك فإن ما يمنع وصول بريد الإنترنت فعلياً هو عدم وجود أي خدمة تستمع على المنفذ 25. سيحتوي صندوق البريد عندئذٍ على الرسائل التي ترسلها تطبيقاتك عبر SMTP فقط. إذا وجّهت DNS إلى الخادم وفتحت المنفذ 25، فأنت تشغّل صندوق استقبال عاماً: سيصل spam خلال أيام، وستنشط هجمات تخمين العناوين التي تخزّن رسالة لكل محاولة، وستُحفظ مرفقات من أشخاص غرباء على القرص من دون أي تصفية.

لماذا لا تُحدَّث قائمة الرسائل إلا عند إعادة تحميل الصفحة؟

يدفع Mailpit الرسائل الجديدة إلى الصفحة المفتوحة عبر WebSocket. إذا كانت كتلة location في nginx تفتقد proxy_http_version 1.1 والرأسين Upgrade وConnection، فلن تتمكن من ترقية ذلك الاتصال، لذلك تُحمَّل الصفحة بشكل طبيعي ثم تتوقف عن التحديث. يستمر وصول البريد، كما تواصل API إرجاعه، ولهذا يبدو صندوق البريد قديماً بدلاً من أن يبدو معطلاً. أضف هذه الأسطر، ثم أعد تحميل nginx، وبعد ذلك أعد تحميل الصفحة.

كيف أمنع صندوق البريد من ملء القرص؟

أبقِ MP_MAX_MESSAGES على قيمة فعلية، وأضف MP_MAX_AGE. الحد الافتراضي هو 500 رسالة، ويؤدي ضبطه على 0 إلى تعطيل الحذف المستند إلى عدد الرسائل. لذلك، ما لم يتم ضبط MP_MAX_AGE، لن تُحذف أي رسائل، وهذا ما يجعل صندوق استقبال عاماً يحتوي على مرفقات ينمو بصمت. يقبل MP_MAX_AGE الساعات أو الأيام، مثل 36h أو 14d. امسح مخزن الرسائل أثناء تنظيف CI باستخدام curl -u qa:yourpassword -X DELETE http://127.0.0.1:8025/api/v1/messages، لأن API تطلب بيانات الاعتماد نفسها التي تطلبها واجهة الويب. ينفذ Inbucket المهمة نفسها باستخدام INBUCKET_STORAGE_RETENTIONPERIOD (72h) وINBUCKET_STORAGE_MAILBOXMSGCAP (300).

هل ينبغي أن أشغّل Mailpit أو Inbucket أو MailHog؟

استخدم Mailpit للمشروعات الجديدة، اعتباراً من October 2026. لا يزال MailHog يعمل، لكن فرعه الافتراضي لم يشهد أي commit منذ August 2022، ولذلك يشحن تبعيات غير مصححة. تتم صيانة Inbucket بنشاط (3.2.0، October 2026)، وهو الخيار الأفضل عندما يحتاج الاختبار إلى POP3، لأن خادم POP3 في Mailpit لا يبدأ إلا بعد تزويده ببيانات اعتماد، إما في ملف كلمات مرور أو في المتغير MP_POP3_AUTH. يستخدم Mailpit المنافذ نفسها التي يستخدمها MailHog، وهي 1025 و8025، لذلك لا يتطلب استبدال MailHog سوى تغيير اسم image واحد في ملف Compose.