SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor

VPS-এ নিজের ডিসপোজেবল ইমেইল ইনবক্স সেটআপ করার নিয়ম

Docker Compose এবং Mailpit ব্যবহার করে কীভাবে নিজের VPS-এ একটি নিরাপদ টেস্ট ইমেইল ইনবক্স তৈরি করবেন তা জানুন। ভুলবশত গ্রাহকের কাছে মেইল যাওয়া রোধ করতে এই পদ্ধতিটি কার্যকর।

ডিসপোজেবল ইমেইল ইনবক্স কী

একটি ডিসপোজেবল ইমেইল ইনবক্স হলো একটি ছোট SMTP (simple mail transfer protocol) সার্ভার, যা যেকোনো ঠিকানার মেইল গ্রহণ করে কিন্তু তা কোথাও পাঠায় না। আপনার স্টেজিং অ্যাপ্লিকেশন কোনো প্রকৃত মেইল প্রোভাইডারের পরিবর্তে এখানে মেইল পাঠায় এবং প্রতিটি বার্তা সেখানেই আটকে থাকে। আপনি একটি ওয়েব ইন্টারফেসের মাধ্যমে মেইলগুলো পড়তে পারেন, তাই ভুল প্রাপকের তালিকা বা ত্রুটিপূর্ণ টেমপ্লেটের কারণে কোনো সমস্যা হয় না, কারণ মেইলটি কখনোই এই ইনবক্সের বাইরে যায় না।

এই নির্দেশিকাটি একটি একক VPS-এ Docker Compose ব্যবহার করে এমন একটি ইনবক্স তৈরি করার প্রক্রিয়া দেখায়। Mailpit এখানে ক্যাচ-অল সিঙ্ক হিসেবে কাজ করে। এর SMTP লিসেনার এমনভাবে বাইন্ড করা থাকে যাতে শুধুমাত্র আপনার অ্যাপ্লিকেশনই সেখানে পৌঁছাতে পারে। এর ওয়েব ইন্টারফেসটি nginx-এর পেছনে transport layer security (TLS) এবং একটি পাসওয়ার্ড দিয়ে সুরক্ষিত থাকে এবং একটি রিটেনশন লিমিট ডিস্ক পূর্ণ হওয়া থেকে রক্ষা করে। আপনি যদি Compose-এর সাথে পরিচিত না হন, তবে the Compose basics for a VPS নিবন্ধটি দেখুন, যেখানে এই নির্দেশিকায় ব্যবহৃত ফাইল লেআউট সম্পর্কে আলোচনা করা হয়েছে।

এর ফলাফল একটি টেস্ট টুল, কোনো মেইল সার্ভার নয়। এতে কোনো অ্যাকাউন্ট, ডেলিভারি বা স্প্যাম ফিল্টারিং নেই। প্রকৃত ব্যবহারকারীদের জন্য আসল মেইলবক্স তৈরি করা a full mail server such as Mailcow ব্যবহারের মতো একটি অনেক বড় কাজ।

Mailpit বনাম Inbucket বনাম MailHog: কোন sink চালাবেন

এই কাজের জন্য তিনটি টুল আছে। এদের মধ্যে পার্থক্য হলো রক্ষণাবেক্ষণের অবস্থা, কোন port-এ তারা listen করে, এবং কোনো message গ্রহণ করার পর সেটি নিয়ে তারা কী করতে পারে। নিচের version-গুলো October 2026-এ যাচাই করা হয়েছে।

MailHog (mailhog/mailhog) SMTP-এর জন্য 1025 port-এ listen করে এবং interface 8025 port-এ পরিবেশন করে। এটি এখনও কাজ করে। এর default branch-এ August 2022-এর পর কোনো commit হয়নি এবং tracker-এ 200-এর বেশি open issue আছে। তাই test path-এ unpatched dependency চালাতে হবে। এটি দিয়ে নতুন কাজ শুরু করবেন না।

Inbucket (inbucket/inbucket) SMTP-এর জন্য 2500 port-এ, web interface-এর জন্য 9000 port-এ এবং POP3 (post office protocol version 3)-এর জন্য 1100 port-এ listen করে। Version 3.2.0 10 October 2026-এ release হয়েছে। এটি /storage-এর অধীনে message file হিসেবে সংরক্ষণ করে এবং নিজে সেগুলো prune করে: image-টি INBUCKET_STORAGE_RETENTIONPERIOD=72h এবং INBUCKET_STORAGE_MAILBOXMSGCAP=300 সেট করে। HTTP call-এর বদলে POP3 client library ব্যবহার করে কোনো test-কে mail সংগ্রহ করতে হলে এটি বেছে নিন।

Mailpit (axllent/mailpit) MailHog-এর একই port, 1025 এবং 8025, ব্যবহার করে। তাই application config পরিবর্তন না করেই MailHog-এর বদলে এটি ব্যবহার করা যায়। Version 1.31.6 15 October 2026-এ release হয়েছে; 1.30 line August-এ 1.30.7-এ শেষ হয়েছে। এই guide-এর প্রয়োজনীয় ফিচারগুলো এটি binary-এর মধ্যেই দেয়: web interface ও API (application programming interface)-এর জন্য password file, message cap, age cap এবং recipient filter। এই guide-এর বাকি অংশে Mailpit চালানো হবে।

ক্যাচ-অল (catch-all) যেভাবে কাজ করে এবং কেন এতে DNS-এর কোনো ভূমিকা নেই

আপনার অ্যাপ্লিকেশন এখানে ইমেইল পাঠানোর গন্তব্য খোঁজে না। আপনি এটিকে একটি host এবং একটি port প্রদান করেন, এটি একটি TCP সংযোগ খোলে এবং RCPT TO:<anyone@example.test> ঘোষণা করে। Mailpit সেই প্রাপককে গ্রহণ করে, বার্তাটি সংরক্ষণ করে এবং কোথাও ফরোয়ার্ড করে না। ডোমেইনটি কখনোই resolve করা হয় না, তাই example.test কাজ করে যদিও .test একটি সংরক্ষিত নাম যা ডোমেইন নেম সিস্টেমে (DNS) কোথাও নেই।

এটিই সম্পূর্ণ প্রক্রিয়া এবং এই কারণেই ইনবক্সটি ডিফল্টভাবে নিরাপদ। এতে কোনো MX (mail exchanger) রেকর্ডের প্রয়োজন হয় না, কোনো ডেলিভারির চেষ্টা করা হয় না এবং কোনো বার্তাই কোনো প্রকৃত ব্যক্তির কাছে পৌঁছাতে পারে না।

আপনার স্টেজ অ্যাপটিকে সিঙ্কের দিকে নির্দেশ করুন

অ্যাপটি যখন একই Compose প্রজেক্টে কন্টেইনার হিসেবে চলে, তখন এর SMTP host-কে mailpit-এ সেট করুন। আর যখন এটি হোস্ট মেশিনে চলে, তখন এটিকে 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 ফাইল

প্রথমে directory এবং web interface-এর জন্য একটি password file তৈরি করুন। htpasswd -B একটি bcrypt hash লেখে, এবং Mailpit bcrypt ও plain text—দুই ধরনের password-ই পড়তে পারে।

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$$'

দুইটি dollar sign থাকা কোনো typo নয়। Compose একটি একক $-কে expand করার জন্য variable-এর শুরু হিসেবে পড়ে। তাই container-এ একটি literal dollar পাঠাতে $$ ব্যবহার করতে হয়। Mailpit-এর কাছে regex-টি @example\.test$ হিসেবে পৌঁছায়।

এটি start করুন এবং health state পরীক্ষা করুন:

docker compose up -d
docker compose ps

STATUS column-এ Up ... (healthy) লেখা থাকা উচিত। Image-টি নিজস্ব healthcheck নিয়ে আসে, যা প্রতি 15 সেকেন্ডে /mailpit readyz চালায়। তাই কোনো container starting অবস্থায় থেকে গেলে বা unhealthy হয়ে গেলে সাধারণত container-এর ভিতরে 8025 port-এ service চলমান থাকে না। এই check MP_ environment variable থেকে settings নেয়, কিন্তু command-line flag থেকে নেয় না। এ কারণেই এই guide-এ সবকিছু environment:-এর অধীনে সেট করা হয়েছে। পরিবর্তে options-কে flag হিসেবে পাঠালে Mailpit স্বাভাবিকভাবে service দেওয়ার সময় check unhealthy report করতে পারে। অন্য কিছু পরিবর্তন করার আগে docker compose logs mailpit পড়ুন।

প্রকাশিত দুইটি port mapping-এই একটি address থাকে, এবং সেই address-ই security control হিসেবে কাজ করে। Container-এর ভিতরে Mailpit 0.0.0.0-এ listen করে, যা ঠিক আছে, কারণ container-এর নিজস্ব network namespace থাকে। Mapping-এর বাম পাশ নির্ধারণ করে বাইরে থেকে কে এটিতে পৌঁছাতে পারবে। 8025:8025 লিখলে Docker host-এর সব address-এ bind করে, public address-সহ।

আপনার staging app যদি একই file-এর একটি service হয়, তাহলে 1025 mapping সম্পূর্ণভাবে মুছে দিন এবং app-কে port 1025-এ mailpit hostname-এ point করুন। একই Compose network-এর container-গুলো সরাসরি একে অপরের কাছে পৌঁছাতে পারে। ফলে SMTP port host-এ একেবারেই আসে না। Compose network কীভাবে service name resolve করে অংশে এই lookup ব্যাখ্যা করা হয়েছে।

একটি মেসেজ পাঠান এবং সেটি পৌঁছেছে কি না যাচাই করুন

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 এবং ওয়েব ইন্টারফেসকে সুরক্ষিত রাখে। ইনবক্স রিড করে এমন যেকোনো টেস্টের ক্ষেত্রেও এই ক্রেডেনশিয়ালগুলো পাঠাতে হবে।

Python স্ক্রিপ্ট থেকে একটি ConnectionRefusedError আসার অর্থ হলো 127.0.0.1:1025-এ কোনো কিছু লিসেন করছে না। আপনি যদি SMTP ম্যাপিং সরিয়ে ফেলেন তবে এটিই প্রত্যাশিত ফলাফল, এবং সেক্ষেত্রে চেকটি একই Compose নেটওয়ার্কের একটি কন্টেইনার থেকে চালাতে হবে।

nginx ব্যবহার করে password দিয়ে web interface প্রকাশ করুন

Interfaceটি বর্তমানে শুধু loopback address-এ সাড়া দেয়। nginx TLS termination করে এবং কোনো অনুরোধ সেখানে পৌঁছানোর আগে password চায়।

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 দিয়ে syntax check করার পরে reload করুন। এটি আপনার প্রথম proxy হলে reverse proxy block-এর প্রতিটি directive কী করে একবার পড়ে নেওয়া উপযোগী।

nginx file এবং data/ui-auth-এ একই username ও password ব্যবহার করুন। nginx browser-এর Authorization header upstream-এ পাঠায়, তাই একই credentials একটি prompt-এর মাধ্যমে উভয় check পূরণ করে। আলাদা credentials ব্যবহার করলে browser-এ এক সেট credentials থেকে যায়, যা দ্বিতীয় check প্রত্যাখ্যান করে।

Upgrade এবং Connection header শুধু দেখানোর জন্য নয়। Mailpit একটি খোলা page-এ WebSocket-এর মাধ্যমে নতুন mail পাঠায়, এবং ওই header ছাড়া HTTP/1.1 ব্যবহার করা proxy connection upgrade করতে পারে না। তখন page সঠিকভাবে load হয়, কিন্তু আর পরিবর্তিত হয় না: mail আসে, API তা দেখায়, তবে reload না করা পর্যন্ত list স্থির থাকে।

দুটি lock-ই রাখুন। nginx-এর password public address সুরক্ষিত রাখে, আর MP_UI_AUTH_FILE সরাসরি port 8025 সুরক্ষিত রাখে। এটি গুরুত্বপূর্ণ, কারণ আপনার staging app যতগুলি password reset link তৈরি করেছে, retention যতক্ষণ সেগুলি সংরক্ষণ করে ততক্ষণ ওই interface-এ সেগুলি পড়া যায়।

Sink-কে কখনও open relay হতে দেবেন না

Open relay হলো এমন একটি SMTP server, যা যেকোনো ব্যক্তির কাছ থেকে message নিয়ে যেকোনো destination-এ তা forward করে। Spammer-রা নিয়মিত open relay খোঁজে। আপনার address-এ এমন server পাওয়া গেলে abuse report এবং account suspension হতে পারে।

Mailpit out of the box open relay নয়, কারণ default অবস্থায় এটি কোনো message onward পাঠায় না। এর outbound feature দুটি, এবং configure না করা পর্যন্ত দুটিই বন্ধ থাকে। MP_SMTP_RELAY_CONFIG একটি relay configuration file নির্দেশ করে এবং interface-এ release action চালু করে। এর মাধ্যমে আপনি নির্বাচিত message ওই server দিয়ে পাঠাতে পারেন। Automatic relaying চালু করতে আরও একটি switch প্রয়োজন: MP_SMTP_RELAY_ALL (default false) অথবা MP_SMTP_RELAY_MATCHING। MP_SMTP_FORWARD_CONFIG অথবা MP_SMTP_FORWARD_* variable দিয়ে forwarding সেট করলে প্রতিটি incoming message আপনার নির্ধারিত fixed address-গুলোতে copy হয়। এগুলো সব unset রাখা একটি সচেতন সিদ্ধান্ত।

এই বৈশিষ্ট্য নষ্ট হওয়ার দুটি উপায় আছে। MP_SMTP_RELAY_ALL চালু করলে প্রতিটি incoming message তার original recipient-দের কাছে পাঠানো হয় এবং MP_SMTP_ALLOWED_RECIPIENTS উপেক্ষা করা হয়। এরপর SMTP port internet-এ expose করলে আপনি কার্যকর open relay তৈরি করেছেন। MP_SMTP_RELAY_MATCHING একই কাজ করে, তবে শুধু pattern-এর সঙ্গে মেলা recipient-দের জন্য। Relay ছাড়া port expose করলে অপরিচিতরা আপনার মাধ্যমে mail পাঠাতে পারবে না। তবে তারা আপনার storage পূর্ণ করতে এবং আপনার team যে interface-এ আস্থা রাখে সেখানে content ঢোকাতে পারবে। Forwarding চালু থাকলে তাদের message copy হিসেবে যেকোনো inbox-এ পৌঁছাবে।

Docker host-এ সমস্যাটি সাধারণত firewall নিয়ে। Port publish করলে Docker নিজস্ব rule nat table-এ লেখে। Container-এর উদ্দেশে আসা traffic সেখানে ufw (uncomplicated firewall) rule কার্যকর হওয়ার আগেই match হয়। sudo ufw deny 1025/tcp সফলতার বার্তা দেখালেও কোনো পরিবর্তন করে না। Docker কীভাবে ufw-এর নিয়ম এড়িয়ে সরাসরি port publish করে chain-এর ক্রম ব্যাখ্যা করে।

সমাধান firewall rule নয়, mapping-এ ব্যবহৃত address। বাস্তবে কী bind করা আছে তা পরীক্ষা করুন:

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

সঠিক output-এ 127.0.0.1:1025 এবং 127.0.0.1:8025 দেখা যাবে। 0.0.0.0:1025 লেখা কোনো line-এর অর্থ হলো mapping থেকে address হারিয়ে গেছে এবং sink internet-এ listen করছে। অন্য একটি machine থেকে nc -vz mail-test.example.com 1025 চালালে তা timeout হওয়া বা refused হওয়া উচিত।

Application অন্য server-এ থাকলে দুই server সংযোগ করাতে 1025 খুলবেন না। দুই machine-কে private network বা VPN tunnel-এ রাখুন এবং mapping-টি সেই interface address-এ bind করুন।

শুধু প্রকৃত inbound mail চাইলে MX record প্রকাশ করুন

একটি MX (mail exchanger) record অন্য mail server-কে জানায়, কোন host কোনো domain-এর mail গ্রহণ করে। এটি বাদ দিলেই নিজে থেকে mail বন্ধ হয় না: কোনো domain-এ MX record না থাকলে RFC 5321-এর section 5.1 অনুযায়ী sending server সেই domain-এর address (A বা AAAA) record ব্যবহার করে fallback করে, এবং web interface-এর জন্য আপনি যে hostname-টি এই box-এ নির্দেশ করেছেন, তার একটি address record আছে। Internet mail ঠেকিয়ে রাখে এই বিষয়টি যে এই box-এ port 25-এ কিছু listen করছে না। Inbox-এ শুধু আপনার নিজস্ব application জমা দেওয়া mail থাকে, test mailbox-এর উদ্দেশ্যও সেটিই।

প্রকৃত mail গ্রহণ করতে হলে এমন একটি DNS record দরকার, যা sending server-কে এই box-এ নির্দেশ করে (একটি MX record, অথবা যে address record-এ তারা fallback করে), port 25-এ Mailpit-এর listening (MP_SMTP_BIND_ADDR=0.0.0.0:25), এবং ওই port খোলা থাকা। তখন আপনি domain-এর প্রতিটি address-এর জন্য একটি public catch-all চালাচ্ছেন। এর পরিণতি স্পষ্টভাবে বুঝে নিন।

  • Record প্রকাশের কয়েক দিনের মধ্যেই spam শুরু হবে, কারণ harvester-রা DNS পড়ে। এরপর dictionary attack সাধারণ নামগুলো একে একে পরীক্ষা করবে এবং প্রতিটি প্রচেষ্টার জন্য একটি message সংরক্ষণ করবে।
  • অচেনা প্রেরকের attachment আপনার disk-এ আসবে এবং সেখানেই থাকবে। কোনো কিছু সেগুলো filter করবে না, তাই অজানা sender-এর archive আপনার নিজস্ব test mail-এর পাশেই পড়ে থাকবে।
  • Domain জানে এমন যে কেউ তৃতীয় পক্ষের service-এ ওই domain-এর একটি address ব্যবহার করে sign up করতে পারবে, এবং confirmation mail আপনার server-এ পৌঁছাবে। Password gate কখনো ফাঁস হলে, inbox পড়ছে যে ব্যক্তি সেই account-গুলোর নিয়ন্ত্রণ পেয়ে যাবে।
  • Retention limit আর শুধু housekeeping-এর বিষয় থাকবে না; এটি load-bearing হয়ে উঠবে, কারণ mail-এর পরিমাণ আর আপনার নিয়ন্ত্রণে থাকবে না।

Deliverability check-এর জন্য প্রকৃত inbound mail প্রয়োজন হলে, এর জন্য একটি dedicated subdomain দিন, MP_MAX_AGE সংক্ষিপ্ত রাখুন, এবং এর সবকিছুকে public হিসেবে বিবেচনা করুন। মানুষ নির্ভর করে এমন mailbox প্রয়োজন হলে filtering ও backup-সহ একটি প্রকৃত mail server চালান।

Retention: সীমাহীন catch-all কীভাবে disk পূর্ণ করে

Mailpit ডিফল্টভাবে 500টি message সংরক্ষণ করে এবং নির্দিষ্ট সীমার অতিরিক্ত পুরোনোগুলো পর্যায়ক্রমে মুছে দেয়। MP_MAX_MESSAGES: 0 এই সীমা বন্ধ করে দেয়। আর MP_MAX_AGE সেট না থাকলে স্বয়ংক্রিয়ভাবে কিছুই মুছে যায় না। এভাবেই কেউ লক্ষ্য না করলেও একটি catch-all disk পূর্ণ করে ফেলে। MP_MAX_AGE একটি সময়সীমা যোগ করে। এটি message cap-এর সঙ্গে বা তার পরিবর্তে ব্যবহার করা যায়। সময়সীমা ঘণ্টা বা দিনে নির্ধারণ করতে হয়, যেমন 36h বা 14d।

MP_DATABASE নির্ধারণ করে এই ডেটা restart-এর পরেও থাকবে কি না। এটি না থাকলে Mailpit একটি temporary file-এ data লেখে। process বন্ধ হলে file-টি মুছে যায়। ফলে প্রতিটি restart-এর পর inbox খালি হয়ে যায়। MP_DATABASE ব্যবহার করলে restart-এর পরেও mail সংরক্ষিত থাকে এবং file-এর আকার বাড়তে থাকে।

Disk space মূলত attachment-এ খরচ হয়। একটি nightly job যদি 300টি test address-এ 2 MB-এর PDF report পাঠায়, তাহলে প্রতি রাতে 600 MB জমে। শুধু message count cap থাকলে এটি সময়মতো সামলানো যায় না। একই volume ব্যবহার করা অন্যান্য data-এর সঙ্গে এই বৃদ্ধির হিসাব মিলিয়ে নিন। কারণ PhotoPrism বা Immich-এর মতো media-heavy প্রতিবেশী service ইতিমধ্যে ছোট VPS disk-এর অধিকাংশ দখল করে থাকতে পারে।

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

cap কার্যকর হওয়ার অপেক্ষা না করে CI run-এর মধ্যে store খালি করুন:

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

Inbucket একই সমস্যা INBUCKET_STORAGE_RETENTIONPERIOD দিয়ে সামলায় (image-এ 72h) এবং INBUCKET_STORAGE_MAILBOXMSGCAP দিয়ে message count সীমিত করে (300)। আপনি যে software-ই চালান, প্রথম 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) করে, এরপর সাবজেক্ট এবং তার ভেতরের লিঙ্কটি যাচাই করে সব মুছে ফেলে। একটি মাত্র রিকোয়েস্ট না পাঠিয়ে ছোট একটি রিট্রাই লুপে পোল করুন, কারণ যে অ্যাপ্লিকেশন ব্যাকগ্রাউন্ড ওয়ার্কারে মেইল কিউ (queue) করে, সেটি Mailpit-এ মেসেজ পৌঁছানোর আগেই সেন্ড কল থেকে ফেরত আসে। একই প্যাটার্ন সেলফ-হোস্টেড এপিআই টেস্টিং এবং মকিং টুলস-এ দেখা যায়, যা সাধারণত স্টেজিং এনভায়রনমেন্টের অন্য অর্ধেক অংশ এবং এটি কখনোই প্রোডাকশন এনভায়রনমেন্টকে স্পর্শ করে না।

FAQ

একটি self-hosted disposable email inbox কি open relay?

Relaying বন্ধ থাকলে নয়। ডিফল্টভাবে Mailpit কোনো বার্তা বাইরে পাঠায় না: MP_SMTP_RELAY_CONFIG শুধু আপনার বেছে নেওয়া বার্তা manual release করার সুবিধা চালু করে, আর automatic relaying-এর জন্য এর সঙ্গে MP_SMTP_RELAY_ALL অথবা MP_SMTP_RELAY_MATCHING-ও প্রয়োজন। এগুলো unset রাখলে port 1025-এ পৌঁছাতে পারা কোনো অপরিচিত ব্যক্তি আপনার server দিয়ে mail পাঠাতে পারবে না। তবে তারা আপনার storage ভরিয়ে দিতে পারে। তাই SMTP port এমন একটি address-এ bind করুন, যেখানে শুধু আপনার application পৌঁছাতে পারে। Compose-এ এটিকে 1025:1025 হিসেবে publish করলে সব host address-এ bind হয়। sudo ufw deny 1025/tcp এটি বন্ধ করবে না, কারণ Docker-এর নিজস্ব nat rule আগে match হয়।

আমার test domain-এর জন্য কি MX record প্রয়োজন?

শুধু তখনই, যখন আপনি internet থেকে mail গ্রহণ করতে চান। তবে MX record না থাকাই একা কোনো lock নয়। MX record না থাকলে sending server-গুলো domain-এর A অথবা AAAA record-এ fallback করে। তাই internet mail বাস্তবে বাইরে রাখে port 25-এ কোনো কিছু listen না করা। এরপর inbox-এ শুধু আপনার নিজস্ব application-গুলো SMTP-এর মাধ্যমে জমা দেওয়া mail থাকবে। DNS-এ box-টির দিকে নির্দেশ করে port 25 খুললে আপনি একটি public catch-all চালাচ্ছেন: কয়েক দিনের মধ্যে spam, প্রতিটি প্রচেষ্টার জন্য একটি করে message সংরক্ষণ করা dictionary attack, এবং কোনো filtering ছাড়াই অপরিচিতদের attachment আপনার disk-এ জমা হবে।

Page reload করলেই কেন message list update হয়?

Mailpit একটি খোলা page-এ WebSocket-এর মাধ্যমে নতুন mail পাঠায়। proxy_http_version 1.1 অনুপস্থিত কোনো nginx location block এবং Upgrade ও Connection header না থাকলে সেই connection upgrade করা যায় না। ফলে page স্বাভাবিকভাবে load হয়, তারপর আর update হয় না। Mail আসতে থাকে এবং API সেটি ফেরত দেয়। তাই inbox-টি broken না হয়ে stale মনে হয়। ওই line-গুলো যোগ করুন, nginx reload করুন, তারপর page reload করুন।

Inbox যাতে disk ভরিয়ে না ফেলে, তা কীভাবে বন্ধ করব?

MP_MAX_MESSAGES-এ একটি বাস্তব সংখ্যা রাখুন এবং MP_MAX_AGE যোগ করুন। ডিফল্ট limit 500টি message। এটিকে 0 সেট করলে count-based deletion বন্ধ হয়। তাই MP_MAX_AGE সেট না থাকলে কোনো কিছু prune হয় না। এভাবেই attachment-সহ catch-all নীরবে বড় হতে থাকে। MP_MAX_AGE hours অথবা days গ্রহণ করে, যেমন 36h অথবা 14d। CI teardown-এ curl -u qa:yourpassword -X DELETE http://127.0.0.1:8025/api/v1/messages দিয়ে store পরিষ্কার করুন, কারণ delete endpoint web interface-এর একই credential গ্রহণ করে। শুধু Send API (POST /api/v1/send)-কে নিজস্ব credential দেওয়া যায়, এবং এটি ঐচ্ছিক। Inbucket একই কাজ করে INBUCKET_STORAGE_RETENTIONPERIOD (72h) এবং INBUCKET_STORAGE_MAILBOXMSGCAP (300) দিয়ে।

আমার কি Mailpit, Inbucket অথবা MailHog চালানো উচিত?

নতুন কাজের জন্য Mailpit ব্যবহার করুন, October 2026 অনুযায়ী। MailHog এখনও চলে, কিন্তু এর default branch-এ August 2022-এর পর কোনো commit হয়নি। তাই এটি unpatched dependency release করে। Inbucket সক্রিয়ভাবে maintained (3.2.0, October 2026) এবং কোনো test-এ POP3 প্রয়োজন হলে এটি ভালো পছন্দ। কারণ Mailpit-এর POP3 server কেবল credential দেওয়ার পর start হয়—হয় password file-এ, নয়তো MP_POP3_AUTH variable-এ। Mailpit MailHog-এর একই port, 1025 এবং 8025, ব্যবহার করে। তাই MailHog প্রতিস্থাপন করতে Compose file-এ শুধু image name পরিবর্তন করতে হয়।

এই লেখাটি কি কাজে লেগেছে?