میلسرور شخصی با Mailcow روی VPS که جیمیل قبول کند
قبل از نصب mailcow پورت 25 خروجی را تست کنید: بسته بودن آن یعنی ایمیل هرگز صف را ترک نمیکند. سپس DNS را طوری تنظیم کنید که خطای 550 5.7.25 PTR جیمیل را متوقف کند.
آنچه میسازید
یک کارساز ایمیل کامل روی دستگاهی که مالک آن هستید: SMTP برای ارسال و دریافت، IMAP تا تلفن و لپتاپ شما همگام بمانند، یک کارخواه وبایمیل، و یک پالایه هرزنامه که به هر پیام در هر دو جهت امتیاز دهد. mailcow-dockerized برنامههای Postfix، Dovecot، Rspamd، وبایمیل SOGo، MariaDB، Redis و یک کارخواه ACME را در یک پشته Docker Compose گرد هم میآورد، بنابراین نرمافزار بخش دشوار کار نیست. آن را در عرض نیم ساعت راهاندازی خواهید کرد.
بخش دشوار همه چیزهای پیرامون آن است. ایمیل تنها خدمتی است که در آن بقیه اینترنت به طور فعال به یک کارساز کاملاً جدید اعتماد ندارند، و شکاف میان «کار میکند» و «Gmail بیسروصدا هر پیامی را میبلعد» به چهار رکورد DNS و یک تنظیم مربوط به اعتبار IP برمیگردد که شاید نتوانید کاملاً آن را کنترل کنید. پیش از آنکه چیزی اجاره کنید، پیشنیازهای زیر را بخوانید. اگر پس از خواندن آنها به این نتیجه رسیدید که زحمت کسب اعتبار ارزشش را ندارد، این پاسخی منطقی است، بررسی ما از آنچه واقعاً ارزش میزبانی شخصی در سال 2026 دارد ایمیل را دقیقاً به همین دلایل در دسته «فقط اگر واقعاً مصمم هستید» قرار میدهد.
پیشنیازها همان پروژه هستند
اگر هر یک از این موارد را نادیده بگیرید، ایمیلی ارسال خواهید کرد که هرگز نمیرسد. در ادامه، این موارد تقریباً به ترتیب میزان دردسرسازی برای افراد فهرست شدهاند:
پورت خروجی 25 باید باز باشد. سرور شما ایمیل را از طریق پورت TCP 25 به Gmail و Microsoft تحویل میدهد. بخش بزرگی از ارائهدهندگان VPS و فضای ابری، پورت خروجی 25 را بهطور پیشفرض برای مقابله با اسپم مسدود میکنند. این مسدودسازی بیصدا انجام میشود؛ هنگام راهاندازی هیچ خطایی رخ نمیدهد، همه چیز سالم به نظر میرسد، اما ایمیل برای همیشه در صف باقی میماند. پیش از نصب هر چیزی، آن را تست کنید. اگر مسدود باشد، تنها راه حل، ارسال یک تیکت پشتیبانی و درخواست از ارائهدهنده برای باز کردن آن است؛ برخی این کار را برای حسابهای قدیمی انجام میدهند و برخی هرگز این کار را نخواهند کرد.
یک IP تمیز با اعتبار قابل استفاده. IPهای VPS بازیافتی اغلب به دلیل اسپم مستأجر قبلی، از قبل در لیستهای سیاه قرار دارند. IP خود را پیش از شروع کار، در سرویسی مانند Spamhaus lookup یا mxtoolbox بررسی کنید. یک IP لیست شده به معنای رد شدنهایی است که با کدنویسی نمیتوانید از آنها عبور کنید.
کنترل DNS به همراه یک رکورد PTR صحیح. شما باید رکوردهایی به زون دامنه خود اضافه کنید و به DNS معکوس (PTR) برای IP سرور نیاز دارید که به نام میزبان ایمیل شما اشاره کند. PTR تقریباً هرگز در پنل DNS شما تنظیم نمیشود، بلکه نزد مالک IP قرار دارد؛ بنابراین در پنل کنترل ارائهدهنده VPS یا از طریق تیکت تنظیم میشود.
6 گیگابایت RAM و 2 هسته مجازی (vCPU) کف قابل قبولی است. حداقل نیاز خود mailcow برای یک نصب خصوصی، 6 گیگابایت RAM به همراه 1 گیگابایت swap است و زمانی که چند کاربر به آن متکی شوند، 8 گیگابایت توصیه میشود. در فضای کمتر از حدود 2.5 گیگابایت، generate_config.sh پیشنهاد غیرفعال کردن اسکنر ویروس ClamAV را میدهد تا هسته سیستم شروع به از بین بردن کانتینرها نکند. برای شروع، 20 گیگابایت فضای SSD به آن اختصاص دهید.
یک نام DNS، نه یک IP خالی. یک نام میزبان مانند mail.example.com انتخاب کنید. همین نام واحد، به MAILCOW_HOSTNAME شما، موضوع گواهی TLS، هدف PTR و بنر SMTP شما تبدیل میشود. آن را در همه جا یکسان نگه دارید.
مرحله 1، اثبات باز بودن پورت خروجی 25
ابتدا این کار را انجام دهید. اگر این مرحله با شکست مواجه شود، تمام تلاشهای دیگر بیهوده است. از سرور مجازی تازه، سعی کنید یک مکالمه SMTP با یک سرور ایمیل واقعی باز کنید:
sudo apt update && sudo apt install -y netcat-openbsd
nc -vz -w 5 gmail-smtp-in.l.google.com 25نتیجه موفقیتآمیز بلافاصله دریافت میشود:
Connection to gmail-smtp-in.l.google.com (142.250.x.x) 25 port [tcp/smtp] succeeded!یک پورت مسدود شده برای پنج ثانیه کامل متوقف میماند و سپس با شکست مواجه میشود:
nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progressآن توقف زمانی، نشانه مسدودیت است. این یک فیلتر شبکه در سمت ارائهدهنده است، نه فایروال شما، بنابراین هیچ تغییر محلی آن را برطرف نمیکند. یک تیکت باز کنید: "لطفاً پورت خروجی TCP 25 را برای سرور مجازی من در <IP> فعال کنید؛ من یک سرور ایمیل قانونی اجرا میکنم." تا زمانی که این دستور "succeeded" برنگرداند، mailcow را نصب نکنید. توجه داشته باشید که پورت ورودی 25 (مسیر دسترسی سایر سرورها به شما) مسیری جداگانه است و معمولاً باز است؛ این سمت خروجی است که ممکن است مسدود باشد.
مرحله 2، اکنون رکوردهای DNS را تنظیم کنید
تغییرات DNS برای انتشار نیاز به زمان دارند، بنابراین هر آنچه میتوانید را پیش از نصب منتشر کنید. فرض کنید دامنه شما example.com,میزبان ایمیل شما mail.example.com و IP شما 10.0.0.10 است. در زون خود، این موارد را ایجاد کنید:
mail.example.com. A 10.0.0.10
mail.example.com. AAAA 2001:db8::10 ; only if you have IPv6
example.com. MX 10 mail.example.com.
example.com. TXT "v=spf1 mx -all"
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:postmaster@example.com"رکورد SPF میگوید «تنها MX من مجاز به ارسال برای این دامنه است، مابقی را رد کن». DMARC را با p=none شروع کنید تا بتوانید گزارشها را بدون برگشت خوردن ایمیلهای خودتان مشاهده کنید؛ پس از اثبات همترازی، آن را به p=quarantine و سپس p=reject محدود کنید. دو رکورد همچنان عمداً وجود ندارند: DKIM که mailcow آن را در مرحله 6 برای شما تولید میکند، و PTR که اکنون در پنل ارائهدهنده خود تنظیم میکنید.
رکورد PTR (DNS معکوس) را برای 10.0.0.10 روی mail.example.com تنظیم کنید، که دقیقاً همان مقدار MAILCOW_HOSTNAME است. این تنها رکوردی است که اغلب افراد فراموش میکنند و ارائهدهندگان بزرگ بر اساس آن رد میکنند. اگر پنل شما فیلد rDNS ندارد، یک درخواست پشتیبانی ثبت کنید.
مرحله 3، نصب Docker
mailcow به Docker Engine به همراه افزونه Compose v2 نیاز دارد. از اسکریپت رسمی Docker استفاده کنید، نه بسته docker.io اوبونتو که اصلاً افزونه Compose را ارائه نمیدهد:
curl -fsSL https://get.docker.com | sudo sh
sudo docker compose versionباید خط Docker Compose version v2.x را ببینید. اگر docker compose version عبارت docker: 'compose' is not a docker command را چاپ کند، Docker Engine نصب شده اما افزونه Compose نصب نیست. افزونه را از مخزن Docker نصب کنید، اسکریپت بالا را دوباره اجرا کنید، یا راهنمای مبانی Docker Compose ما را دنبال کنید که هر دو را از مخزن خود Docker راهاندازی میکند.
مرحله 4، کلون کردن mailcow و تولید پیکربندی
cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
umask
sudo ./generate_config.shابتدا بررسی کنید که umask مقدار 0022 را چاپ کند، mailcow با یک ماسک فایل فرد از ساخت خودداری میکند و یک شل root تازه Ubuntu 24.04 از قبل مقدار 0022 را به شما میدهد. سپس اسکریپت تنها چیزی را که مهم است میپرسد: نام میزبان کاملاً واجد شرایط. مقدار mail.example.com را وارد کنید، این مقدار باید دقیقاً با رکورد A و PTR شما مطابقت داشته باشد. اسکریپت فایل mailcow.conf را مینویسد، که تنها فایل محیطی است که کل پشته از آن میخواند. اگر نیاز به تغییر پورتهای وب (HTTP_PORT, HTTPS_PORT) یا غیرفعال کردن ClamAV روی یک سیستم کوچک دارید، آن را باز کنید:
MAILCOW_HOSTNAME=mail.example.com
HTTP_PORT=80
HTTPS_PORT=443
SKIP_CLAMD=n # set to y to drop the virus scanner on a <2.5 GiB boxSKIP_FTS=y اهرم دیگر روی سیستمی با RAM کم است: جستجوی تمام متن دومین مصرفکننده بزرگ حافظه است که مستندات mailcow نام میبرد و صرف نظر کردن از آن تنها جستجوی متن بدنه را در وبمیل از دست میدهد.
پورتهای HTTP_PORT=80 و HTTPS_PORT=443 را رها کنید مگر اینکه سرویس دیگری روی میزبان قبلاً آنها را اشغال کرده باشد، کلاینت ACME داخلی mailcow برای دریافت گواهی نیاز دارد که پورت 80 از اینترنت قابل دسترس باشد. به همین دلیل است که شما یک راهاندازی جداگانه nginx به همراه Certbot را روی همان سیستم اجرا نمیکنید؛ mailcow گواهیهای TLS خود را به صورت داخلی صادر و تمدید میکند و یک سرویس دوم که پورتهای 80/443 را اشغال کند، این فرآیند را مختل میکند.
مرحله 5، راهاندازی پشته و ورود به سیستم
sudo docker compose pull
sudo docker compose up -d
sudo docker compose psدستور pull تقریباً دو دوجین ایمیج دریافت میکند؛ چند دقیقه به آن زمان دهید. هنگامی که docker compose ps نشان داد هر کانتینر running (یا healthy) است، https://mail.example.com را در یک مرورگر باز کنید. ورود پیشفرض مدیریت، نام کاربری admin و رمز عبور moohoo است. بلافاصله آن رمز عبور را در رابط کاربری مدیریت، در بخش Access → Administrators تغییر دهید. اگر مرورگر هشدار NET::ERR_CERT_AUTHORITY_INVALID را نشان داد، گواهی ACME هنوز صادر نشده است؛ پیش از آنکه فرض کنید خراب است، بخش مربوط به خطای ACME را در زیر ببینید؛ یک گواهی خودامضای موقت برای یکی دو دقیقه اول طبیعی است.
مرحله 6، افزودن دامنه، صندوق پستی و انتشار DKIM
در رابط مدیریتی، صفحه تنظیمات ایمیل را باز کنید (پیکربندی ← تنظیمات ایمیل)، و در زبانه دامنهها روی افزودن دامنه کلیک کرده و example.com را وارد کنید. سپس، در بخش صندوقهای پستی، صندوق پستی را اضافه کنید تا you@example.com با یک گذرواژه ایجاد شود. این یک صندوق پستی فعال است که هماکنون از طریق IMAP قابل دسترسی است.
اکنون کلید DKIM. به پیکربندی ← کلیدهای ARC/DKIM بروید؛ mailcow ممکن است هنگام افزودن دامنه، کلیدی را تولید کرده باشد، و اگر نه، یکی در آنجا تولید کنید، دامنه را انتخاب کنید، انتخابگر dkim را نگه دارید، 2048-بیت را انتخاب کرده و روی افزودن کلیک کنید. مقدار بلند TXT که نشان میدهد را کپی کرده و به صورت زیر منتشر کنید:
dkim._domainkey.example.com. TXT "v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqh...long-key...QAB"صفحه دامنههای mailcow یک دکمه DNS دارد که تمام رکوردهای مورد انتظار را فهرست کرده و یک تیک سبز یا ضربدر قرمز در مقابل آنچه واقعاً منتشر شده نشان میدهد. از آن به عنوان چکلیست خود استفاده کنید، پیش از آزمایش قابلیت تحویل، تمام سطرها را سبز کنید. یک سطر قرمز DKIM پس از انتشار معمولاً به این معناست که کلید به اشتباه در میان بخشهای TXT تقسیم شده است؛ یک کلید 2048-بیتی بلندتر از محدودیت 255 کاراکتری برای یک رشته TXT منفرد است، بنابراین آن را به عنوان یک مقدار منطقی واحد بچسبانید و اجازه دهید میزبان DNS شما آن را برایتان به بخشهایی تقسیم کند.
گام 7، آزمون تحویلپذیری و دستیابی به امتیاز 10 از 10
به mail-tester.com بروید، نشانی تصادفی که نشان میدهد را کپی کنید و یک پیام از صندوق پستی جدید خود به آن بفرستید. وارد وبمیل SOGo در https://mail.example.com/SOGo شوید و از آنجا ارسال کنید. سپس روی «Then check your score» کلیک کنید.
هدف، امتیاز 10 از 10 است. کسر امتیازهای رایج و دلایل آنها:
- SPF همتراز نیست، رکورد
MX/SPF شما وجود ندارد یا IP ارسالکننده را پوشش نمیدهد. TXT مربوط به SPF را دوباره بررسی کنید. - امضای DKIM تأیید نمیشود، TXT مربوط به
dkim._domainkeyوجود ندارد، هنوز در حال انتشار است یا خراب شده است. این شایعترین مورد از قلم افتاده است. - PTR وجود ندارد / PTR ناهمخوان است، DNS معکوس به
mail.example.comحل نمیشود. این مشکل را در ارائهدهنده برطرف کنید. - در یک فهرست مسدودسازی ثبت شده است، این به اعتبار پیشین IP شما برمیگردد. درخواست خروج از فهرست دهید یا یک IP تمیزتر بخواهید.
تا زمانی که این آزمون امتیاز 10 از 10 را نشان ندهد، به Gmail یا Outlook ایمیل واقعی ارسال نکنید. امتیاز پایین بههمراه یک IP جدید، همان چیزی است که باعث میشود دامنه شما از روز اول پرچمگذاری شود.
مرحله 8، اتصال یک سرویسگیرنده ایمیل واقعی
Thunderbird، Apple Mail یا تلفن همراه خود را با این تنظیمات به سرور متصل کنید. میزبان سرور برای همه آنها mail.example.com است:
- IMAP: درگاه 993، SSL/TLS (یا 143 با STARTTLS)
- ارسال SMTP: درگاه 465، SSL/TLS (یا 587 با STARTTLS)
- نام کاربری: آدرس کامل،
you@example.com - گذرواژه: گذرواژه صندوق پستی که تنظیم کردهاید
هرگز ایمیل سرویسگیرنده را از درگاه 25 ارسال نکنید، این درگاه فقط برای ارتباط سرور-به-سرور است، mailcow ارسال تأیید هویتشده روی آن ارائه نمیدهد و سرویسگیرندهای که به آن متصل شود رد میشود. اگر سرویسگیرنده Relay access denied را گزارش دهد، یعنی تلاش میکند روی درگاه 25 یا بدون تأیید هویت ارسال کند؛ آن را با اطلاعات کاربری صندوق پستی خود به 465 یا 587 تغییر دهید.
گام 9، پشتیبانگیری از آنچه واقعاً اهمیت دارد
میلکاو یک اسکریپت پشتیبانگیری ارائه میدهد که از تمام حجمهای دارای حالت، snapshot تهیه میکند. آن را روی یک دیسک خارجی یا یک فضای راه دور mount شده اجرا کنید:
sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \
./helper-scripts/backup_and_restore.sh backup allall شش جزء را ثبت میکند و از دست دادن هر یک به معنای از دست رفتن داده است: vmail (صندوقهای پستی واقعی)، crypt (کلیدهای رمزگشایی vmail که بدون آن بیاستفاده است)، mysql (پایگاه داده MariaDB شامل دامنهها، کاربران، نامهای مستعار و تنظیمات)، redis (وضعیت صف و کش)، rspamd (هرزنامه/غیرهرزنامه یادگرفتهشده) و postfix (صف ایمیل). این اسکریپت درون یک کانتینر کمکی اجرا میشود و بایگانیهای فشرده مینویسد، بنابراین پشتیبانها حتی زمانی که مجموعه فعال است، یکپارچه باقی میمانند. آن را با یک cron job شبانه خودکار کنید و --delete-days 14 را برای حذف مجموعههای قدیمی اضافه نمایید. بازیابی با همان اسکریپت و با restore انجام میشود که snapshotها را فهرست کرده و به شما امکان انتخاب آنچه باید برگردانده شود را میدهد. پشتیبانی که هرگز بازیابی آن را آزمایش نکردهاید، یک امید است، نه یک پشتیبان؛ یک اجرای آزمایشی روی یک VPS موقت انجام دهید.
مرحله 10، بهروزرسانی زمانبندیشده
mailcow از طریق اسکریپت خودش بهروزرسانی میشود که کد جدید را دریافت میکند، mailcow.conf را مهاجرت میدهد، ایمیجها را پیشدریافت میکند و کانتینرها را به ترتیب راهاندازی مجدد میکند:
cd /opt/mailcow-dockerized
sudo ./update.sh --check # reports whether an update exists, changes nothing
sudo ./update.sh # applies itابتدا پشتیبان بگیرید (مرحله 9)، زیرا بازگردانی مهاجرت اسکیما دشوار است. بهروزرسانیها مکرر منتشر میشوند و شامل اصلاحات امنیتی برای سرویسهای روبهاینترنت هستند، بنابراین نگذارید یک سرور ایمیل ماهها عقب بماند. اگر بهروزرسانی کانتینری را ناسالم رها کند، sudo docker compose logs --tail=50 <service>-mailcow نام سرویسی را که نتوانسته بازگردد مشخص میکند.
نکتهای دربارهٔ مقاومسازی
mailcow سرویس netfilter اختصاصی خود (netfilter-mailcow) را اجرا میکند که آیپیهایی را که به پورتهای ایمیل و وبمیل حمله میکنند مسدود میکند، بنابراین بخش ایمیل بهصورت پیشفرض محافظت میشود. این موضوع SSH روی خود میزبان را پوشش نمیدهد، که همچنان در معرض دید است و مورد حملات حدس رمز قرار میگیرد؛ این ساختار را با Fail2ban برای نظارت بر گزارش احراز هویت SSH و ورود فقط با کلید همراه کنید. رابط مدیریت mailcow را پشت یک رمز عبور قوی و، در حالت ایدهآل، خارج از اینترنت عمومی یا پشت VPN نگه دارید.
حالتهای خرابی، همراه با رشتههای دقیق
صفهای ایمیل و عدم تحویل برای همیشه. دستور sudo docker compose exec postfix-mailcow postqueue -p را اجرا کنید، یا صف ایمیل رابط کاربری مدیریت را بخوانید؛ ورودیها با وضعیت deferred و این پیام باقی میمانند:
status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)این یعنی پورت خروجی 25 توسط ارائهدهنده شما مسدود شده است (مرحله 1). هیچ تنظیماتی این مشکل را برطرف نمیکند، یک درخواست پشتیبانی ثبت کنید. این مشکل مربوط به DNS یا TLS نیست؛ نشانه آن کلمه timed out در مقابل یک MX راه دور روی پورت 25 است.
جیمیل همه چیز را اسپم علامتگذاری میکند، یا آن را برمیگرداند. پیام را در جیمیل باز کنید، "Show original" را بزنید و نتایج احراز هویت را بخوانید. dkim=fail یا dkim=none یعنی رکورد TXT مربوط به dkim._domainkey شما وجود ندارد، خراب است، یا هنوز منتشر نشده است، دقیقاً آنچه را صفحه ARC/DKIM نشان میدهد دوباره منتشر کنید و به اندازه TTL صبر کنید. spf=fail یعنی رکوردهای SPF/MX آیپی شما را پوشش نمیدهند. همترازی همه چیز است؛ یک بررسی ناموفق برای فرود در اسپم کافی است.
رد شدن توسط ارائهدهندگان بزرگ در هنگام اتصال. برگشتها یا لاگهای Postfix پیام رد PTR جیمیل را حمل میکنند:
550-5.7.25 [10.0.0.10] The IP address sending this message does not have a PTR
550-5.7.25 record setup, or the corresponding forward DNS entry does not match
550 5.7.25 the sending IP. As a policy, Gmail does not accept messages from IPs
550 5.7.25 with missing PTR records.کد 550 5.7.25 به معنای DNS معکوس گمشده یا ناهماهنگ است. رکورد PTR آیپی خود را در ارائهدهنده روی mail.example.com تنظیم کنید (مرحله 2). رکورد مستقیم (A) و معکوس (PTR) باید مطابقت داشته باشند، و هر دو باید همان نام میزبانی را مشخص کنند که mailcow خود را با آن به سرورهای دیگر معرفی میکند.
مرورگر یک هشدار گواهی نشان میدهد که هرگز برطرف نمیشود. کانتینر acme-mailcow نتوانست یک گواهی واقعی دریافت کند. لاگ آن را بررسی کنید:
sudo docker compose logs acme-mailcow | tail -n 40خطی مانند Cannot validate any hostnames, skipping Let's Encrypt for 1 hour. یا یک خطای challenge یعنی پورت 80 از اینترنت قابل دسترسی نیست، یا رکورد A به این سرور اشاره نمیکند. تأیید کنید که mail.example.com به این ماشین حل میشود، پورتهای 80 و 443 را در هر فایروال میزبان باز کنید، و مطمئن شوید هیچ چیز دیگری آن پورتها را اشغال نکرده است. پس از برطرف کردن علت، کلاینت را با sudo docker compose restart acme-mailcow راهاندازی مجدد کنید به جای اینکه منتظر توقف یک ساعته بمانید.
FAQ
Is self-hosting email actually worth it?
If you want data ownership, unlimited aliases, and full control, yes, mailcow gives you a professional stack for the price of a VPS. But deliverability is a standing chore: IP reputation, DNS alignment, and blocklist monitoring never fully end. For a mission-critical business address where a single day in someone's spam folder costs you, a managed provider is the pragmatic call. Self-host when you value control over convenience and will actually tend it.
How do I know if outbound port 25 is blocked?
Run nc -vz -w 5 gmail-smtp-in.l.google.com 25 from the server. "succeeded!" means it is open; a timed out after the pause means your provider blocks it. This is the single most common reason a self-hosted server can receive mail but never send it, and the only fix is your provider opening the port, no local setting changes it.
Why does my mail still land in Gmail's spam folder?
Almost always a broken authentication chain. Use "Show original" in Gmail and look for spf=pass, dkim=pass, and dmarc=pass. A dkim=fail points at a missing or mangled dkim._domainkey TXT record; a PTR mismatch or a new IP with no sending history also hurts. Get mail-tester.com to 10/10 first, then warm the IP up slowly, a few messages a day building to more, rather than blasting volume on day one.
What exactly do I need to back up?
Run backup_and_restore.sh backup all and keep the whole set off the server. It captures vmail (the mailboxes), crypt (the keys that decrypt them), the MariaDB database (domains, users, aliases, settings), Redis, Rspamd's learned data, and the Postfix queue. The crypt volume is the one people overlook, without it the vmail backup is unreadable ciphertext. Test a restore onto a scratch box at least once.
Can I run mailcow on a 2 GB VPS?
Not comfortably. generate_config.sh offers to disable ClamAV below about 2.5 GiB, and even then Rspamd, ClamAV, Dovecot and MariaDB contend for memory, so you will hit swap and OOM kills under any real load. Treat 6 GiB plus 1 GiB swap as the floor for a stable single-user install, and move to 8 GiB the moment more than a couple of people rely on it.