Cài mailcow trên VPS để Gmail chấp nhận email
Kiểm tra port 25 trước khi cài mailcow: bị chặn thì mail kẹt trong queue. Cấu hình DNS đúng để tránh Gmail trả lỗi 550 5.7.25 do thiếu PTR.
Bạn đang xây dựng
Một mail server hoàn chỉnh trên máy chủ do bạn sở hữu: SMTP để gửi và nhận mail, IMAP để điện thoại và laptop luôn đồng bộ, webmail client và bộ lọc spam chấm điểm mọi message theo cả hai chiều. mailcow-dockerized đóng gói Postfix, Dovecot, Rspamd, SOGo webmail, MariaDB, Redis và ACME client thành một Docker Compose stack, nên phần mềm không phải là phần khó. Bạn sẽ chạy được hệ thống trong nửa giờ.
Phần khó là mọi thứ xung quanh nó. Email là dịch vụ duy nhất mà phần còn lại của Internet chủ động không tin tưởng một server mới, và khoảng cách giữa “chạy được” với “Gmail âm thầm loại bỏ mọi message” phụ thuộc vào 4 bản ghi DNS và 1 thiết lập về uy tín IP mà bạn có thể không hoàn toàn kiểm soát. Hãy đọc các điều kiện tiên quyết bên dưới trước khi thuê bất kỳ thứ gì. Nếu đọc xong và quyết định việc xây dựng uy tín IP không đáng công, đó là một lựa chọn hợp lý. bản tổng quan về những thứ thực sự đáng tự host trong 2026 xếp email vào nhóm “chỉ nên làm nếu bạn thực sự muốn” chính vì các lý do này.
Điều kiện tiên quyết chính là toàn bộ dự án
Chỉ cần thiếu một điều kiện, bạn sẽ gửi email nhưng email không bao giờ đến nơi. Theo thứ tự tương đối về tần suất khiến mọi người gặp sự cố:
Phải mở cổng outbound 25. Server của bạn gửi email đến Gmail và Microsoft qua TCP port 25. Phần lớn VPS và cloud provider mặc định chặn outbound 25 để chống spam. Việc chặn này không báo lỗi: lúc boot không có gì bất thường, mọi thứ vẫn có vẻ hoạt động, nhưng email chỉ nằm mãi trong queue. Hãy kiểm tra trước khi cài bất kỳ thứ gì. Nếu cổng bị chặn, cách khắc phục duy nhất là gửi support ticket và yêu cầu provider mở cổng. Một số provider sẽ mở cho các account đã hoạt động đủ lâu, một số khác thì không bao giờ mở.
IP sạch và có reputation có thể sử dụng. IP VPS được tái phân bổ thường đã nằm trong blocklist do tenant trước đó gửi spam. Hãy kiểm tra IP của bạn bằng dịch vụ như Spamhaus lookup hoặc mxtoolbox trước khi quyết định. IP bị liệt kê sẽ dẫn đến các lần từ chối mà bạn không thể xử lý bằng code.
Có quyền quản lý DNS và bản ghi PTR chính xác. Bạn cần thêm record vào zone của domain, đồng thời cần reverse DNS (PTR) cho IP của server trỏ ngược về mail hostname. PTR gần như không bao giờ được cấu hình trong DNS panel. Bản ghi này do đơn vị sở hữu IP quản lý, nên bạn phải cấu hình trong control panel của VPS provider hoặc gửi ticket yêu cầu.
6 GiB RAM và 2 vCPU là mức tối thiểu thoải mái. Mức tối thiểu chính thức của mailcow cho một cài đặt private là 6 GiB RAM cộng với 1 GiB swap. mailcow khuyến nghị 8 GiB khi có một nhóm nhỏ người dùng phụ thuộc vào hệ thống. Khi RAM thấp hơn khoảng 2.5 GiB, generate_config.sh sẽ đề xuất tắt virus scanner ClamAV để kernel không bắt đầu kill các container. Hãy cấp 20 GB SSD ngay từ đầu.
Cần có DNS name, không dùng IP trần. Chọn một hostname như mail.example.com. Tên này đồng thời trở thành MAILCOW_HOSTNAME, subject của TLS certificate, đích của PTR và SMTP banner. Hãy dùng nhất quán tên này ở mọi nơi.
Bước 1, Xác nhận cổng outbound 25 đang mở
Làm bước này trước. Nếu bước này thất bại thì mọi việc khác đều vô ích. Từ VPS mới, thử mở một phiên SMTP với mail server thật:
sudo apt update && sudo apt install -y netcat-openbsd
nc -vz -w 5 gmail-smtp-in.l.google.com 25Kết quả hoạt động sẽ trả về ngay:
Connection to gmail-smtp-in.l.google.com (142.250.x.x) 25 port [tcp/smtp] succeeded!Cổng bị chặn sẽ treo đủ năm giây rồi mới thất bại:
nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progressTimeout đó chính là dấu hiệu cổng bị chặn. Đây là bộ lọc mạng ở phía nhà cung cấp, không phải firewall của bạn, nên không có thay đổi cục bộ nào khắc phục được. Hãy mở ticket với nội dung: "Please enable outbound TCP port 25 for my VPS at <IP>; I am running a legitimate mail server." Không cài mailcow cho đến khi lệnh này trả về "succeeded". Lưu ý rằng inbound 25 (các server khác kết nối đến bạn) là một đường kết nối riêng và thường đang mở; nhà cung cấp thường giới hạn phía outbound.
Bước 2, Thiết lập các bản ghi DNS ngay
Thay đổi DNS cần thời gian để propagate, vì vậy hãy publish mọi thứ có thể trước khi cài đặt. Giả sử domain của bạn là example.com, mail host là mail.example.com và IP là 10.0.0.10. Trong zone, hãy tạo:
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"Bản ghi SPF có nghĩa là “chỉ MX của tôi được phép gửi cho domain này, từ chối các nguồn còn lại”. Bắt đầu DMARC với p=none để theo dõi các report mà không làm bounce mail của chính bạn; chuyển sang p=quarantine rồi p=reject sau khi xác nhận alignment hoạt động đúng. Cố ý còn thiếu 2 bản ghi: DKIM, do mailcow tạo cho bạn ở Bước 6, và PTR, bạn thiết lập ngay trong panel của provider.
Đặt PTR (reverse DNS) của 10.0.0.10 thành mail.example.com, chính xác là giá trị MAILCOW_HOSTNAME. Đây là bản ghi mà đa số mọi người quên, và các provider lớn sẽ từ chối mail nếu thiếu hoặc sai bản ghi này. Nếu panel không có trường rDNS, hãy mở ticket.
Bước 3: Cài đặt Docker
mailcow cần Docker Engine cùng plugin Compose v2. Dùng convenience script chính thức của Docker thay vì package docker.io của Ubuntu. Package này hoàn toàn không có plugin Compose:
curl -fsSL https://get.docker.com | sudo sh
sudo docker compose versionBạn sẽ thấy một dòng Docker Compose version v2.x. Nếu docker compose version in ra docker: 'compose' is not a docker command, Docker Engine đã được cài đặt nhưng plugin Compose chưa có. Hãy cài plugin từ repository của Docker, chạy lại script ở trên hoặc làm theo hướng dẫn cơ bản về Docker Compose của chúng tôi, trong đó thiết lập cả hai thành phần từ apt repository của Docker.
Bước 4, Clone mailcow và tạo cấu hình
cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
umask
sudo ./generate_config.shTrước hết, kiểm tra umask phải in ra 0022. mailcow sẽ từ chối build nếu file mask có giá trị lẻ, còn một root shell mới trên Ubuntu 24.04 đã có sẵn 0022. Sau đó, script hỏi thông tin quan trọng nhất: hostname đầy đủ. Nhập mail.example.com; giá trị này phải khớp chính xác với A record và PTR. Script ghi cấu hình vào mailcow.conf, file môi trường duy nhất mà toàn bộ stack sử dụng. Mở file này nếu cần đổi các cổng web (HTTP_PORT, HTTPS_PORT) hoặc tắt ClamAV trên máy có cấu hình thấp:
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 là tùy chọn điều chỉnh khác trên máy có ít RAM: tìm kiếm toàn văn là tác vụ ngốn bộ nhớ nhiều thứ hai theo tài liệu mailcow, và bỏ qua tính năng này chỉ khiến bạn không thể tìm trong nội dung thư trên webmail.
Giữ nguyên HTTP_PORT=80 và HTTPS_PORT=443, trừ khi một dịch vụ khác trên host đã sử dụng chúng. ACME client tích hợp của mailcow cần cổng 80 có thể truy cập từ Internet để lấy chứng chỉ. Vì vậy, không chạy thiết lập nginx và Certbot riêng trên cùng máy; mailcow tự cấp và gia hạn TLS bên trong, còn một dịch vụ thứ hai chiếm cổng 80/443 sẽ làm hỏng cấu hình đó. Điều tương tự áp dụng cho mọi dịch vụ khác cần public web front end. Vì vậy, mô hình như Halcyon biến thư viện Jellyfin thành một cửa hàng băng video kiểu thập niên 90, vốn cần reverse proxy riêng trên cổng 443, phải chạy trên host khác. Ngoại lệ là dịch vụ không yêu cầu public web port: RustDesk relay tự host chỉ lắng nghe dải cổng riêng từ 21115 đến 21119, nên có thể dùng chung máy nếu băng thông còn đủ.
Bước 5, khởi động stack và đăng nhập
sudo docker compose pull
sudo docker compose up -d
sudo docker compose psLệnh pull sẽ tải khoảng hai chục image; hãy chờ vài phút. Khi docker compose ps hiển thị mọi container đều ở trạng thái running (hoặc healthy), mở https://mail.example.com trong trình duyệt. Thông tin đăng nhập admin mặc định là username admin, password moohoo. Hãy đổi password ngay trong giao diện admin tại Access → Administrators. Nếu trình duyệt cảnh báo NET::ERR_CERT_AUTHORITY_INVALID, chứng chỉ ACME chưa được cấp. Hãy xem phần lỗi ACME bên dưới trước khi kết luận hệ thống bị lỗi; self-signed certificate tạm thời là bình thường trong một hoặc hai phút đầu.
Bước 6, thêm domain, mailbox và công bố DKIM
Trong admin UI, mở trang Mail Setup (Configuration → Mail Setup). Tại tab Domains, nhấp Add domain rồi nhập example.com. Tiếp theo, trong mục Mailboxes, nhấp Add mailbox để tạo you@example.com cùng với password. Mailbox này đã hoạt động và có thể truy cập qua IMAP.
Tiếp theo là DKIM key. Vào Configuration → ARC/DKIM keys. mailcow có thể đã tự tạo key khi bạn thêm domain. Nếu chưa có, hãy tạo key tại đây, chọn domain, giữ selector dkim, chọn 2048-bit rồi nhấp Add. Sao chép giá trị TXT dài mà giao diện hiển thị và công bố nó dưới dạng:
dkim._domainkey.example.com. TXT "v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqh...long-key...QAB"Trang Domains của mailcow có nút DNS, liệt kê mọi record cần thiết và hiển thị dấu tick màu xanh hoặc dấu X màu đỏ tương ứng với trạng thái thực tế đã công bố. Dùng trang này làm checklist. Đảm bảo mọi dòng đều màu xanh trước khi kiểm tra khả năng gửi mail. DKIM vẫn hiển thị màu đỏ sau khi công bố thường có nghĩa là key đã bị chia sai thành các đoạn TXT. Key 2048-bit dài hơn giới hạn 255 ký tự của một chuỗi TXT đơn, vì vậy hãy dán nó dưới dạng một giá trị logic duy nhất và để DNS host tự chia thành các đoạn.
Bước 7, Kiểm tra khả năng gửi mail và đạt 10/10
Truy cập mail-tester.com, sao chép địa chỉ ngẫu nhiên được hiển thị, rồi gửi một message đến địa chỉ đó từ mailbox mới. Đăng nhập vào SOGo webmail tại https://mail.example.com/SOGo và gửi từ đó. Sau đó nhấp vào "Then check your score".
Mục tiêu là đạt 10/10. Các lỗi thường bị trừ điểm và nguyên nhân:
- SPF không khớp, bản ghi
MX/SPF bị thiếu hoặc không bao gồm IP gửi mail. Kiểm tra lại SPF TXT. - Không xác minh được chữ ký DKIM, bản ghi TXT của
dkim._domainkeybị thiếu, chưa propagate xong hoặc bị sai nội dung. Đây là lỗi thường gặp nhất. - Không có PTR / PTR không khớp, reverse DNS không phân giải về
mail.example.com. Sửa tại nhà cung cấp. - IP có trong blocklist, do reputation trước đó của IP. Yêu cầu gỡ khỏi danh sách hoặc xin một IP sạch hơn.
Không gửi mail thật đến Gmail hoặc Outlook cho đến khi kết quả đạt 10/10. Điểm thấp cùng với IP mới có thể khiến domain của bạn bị đánh dấu ngay từ ngày đầu.
Bước 8, Kết nối mail client thực
Cấu hình Thunderbird, Apple Mail hoặc điện thoại của bạn kết nối đến server bằng các thiết lập sau. Host của server cho tất cả client là mail.example.com:
- IMAP: port 993, SSL/TLS (hoặc 143 với STARTTLS)
- SMTP submission: port 465, SSL/TLS (hoặc 587 với STARTTLS)
- Username: địa chỉ đầy đủ,
you@example.com - Password: mật khẩu mailbox bạn đã đặt
Không bao giờ gửi mail từ client qua port 25. Port này chỉ dùng cho kết nối server-to-server. mailcow không cung cấp authenticated submission trên port đó, nên client kết nối đến port này sẽ bị từ chối. Nếu client báo Relay access denied, client đang cố gửi qua port 25 hoặc không có authentication. Hãy chuyển sang port 465 hoặc 587 và dùng credentials của mailbox.
Bước 9, Sao lưu những dữ liệu thực sự quan trọng
mailcow có sẵn script sao lưu, tạo snapshot cho mọi volume có trạng thái. Chạy script này để lưu vào một disk bên ngoài hoặc một remote đã mount:
sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \
./helper-scripts/backup_and_restore.sh backup allall sao lưu 6 thành phần. Mất bất kỳ thành phần nào cũng làm mất dữ liệu: vmail (các mailbox thực tế), crypt (các key dùng để giải mã vmail; không có thành phần này thì key vô dụng), mysql (MariaDB lưu domain, user, alias và các thiết lập), redis (trạng thái queue và cache), rspamd (dữ liệu spam/ham đã học), và postfix (mail queue). Script chạy bên trong một helper container và ghi các archive đã nén, nên backup vẫn nhất quán ngay cả khi stack đang hoạt động. Tự động chạy script bằng cron hằng đêm và thêm --delete-days 14 để xóa các bộ backup cũ. Cron job khi bị lỗi thường sẽ fail im lặng, vì vậy hãy gửi exit status của script đến nơi bạn thực sự có thể nhận biết; một ntfy server tự host có thể dùng một lệnh curl trên cùng cron entry để gửi thông báo lỗi đến điện thoại ngay trong đêm xảy ra lỗi. Restore dùng chính script này với restore. Tùy chọn đó liệt kê các snapshot và cho phép bạn chọn dữ liệu cần khôi phục. Một backup chưa từng được test restore chỉ là hy vọng, không phải backup. Hãy chạy thử một dry run trên một VPS tạm.
Bước 10, cập nhật theo lịch
mailcow cập nhật bằng script riêng. Script này kéo code mới, migrate mailcow.conf, tải trước các image và lần lượt restart các container:
cd /opt/mailcow-dockerized
sudo ./update.sh --check # reports whether an update exists, changes nothing
sudo ./update.sh # applies itHãy backup trước (Bước 9), vì việc migrate schema rất khó đảo ngược. Các bản cập nhật thường xuyên được phát hành và bao gồm các bản sửa lỗi bảo mật cho những daemon public Internet, vì vậy không để mail server không được cập nhật trong nhiều tháng. Nếu một bản cập nhật khiến một container chuyển sang trạng thái không healthy, sudo docker compose logs --tail=50 <service>-mailcow sẽ cho biết daemon không khởi động lại thành công.
Lưu ý về hardening
mailcow tự chạy service netfilter (netfilter-mailcow) để ban các IP liên tục gửi request vào các cổng mail và webmail, nên phần mail đã được bảo vệ sẵn. Tuy nhiên, cơ chế này không bảo vệ SSH trên chính host. SSH vẫn được expose và vẫn bị brute-force. Hãy kết hợp bản triển khai này với Fail2ban monitor log xác thực SSH và chỉ cho phép đăng nhập bằng key. Đặt mật khẩu mạnh cho admin UI của mailcow. Tốt nhất là không expose UI này ra public Internet hoặc đặt nó phía sau VPN.
Các chế độ lỗi và chuỗi chính xác
Hàng đợi mail tăng lên nhưng không bao giờ gửi được. Chạy sudo docker compose exec postfix-mailcow postqueue -p hoặc xem hàng đợi mail trong admin UI; các entry vẫn ở trạng thái deferred với:
status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)Đó là do nhà cung cấp chặn port outbound 25 (Bước 1). Không có cấu hình nào sửa được lỗi này; hãy mở ticket. Lỗi này không liên quan đến DNS hay TLS. Dấu hiệu nhận biết là từ timed out xuất hiện khi kết nối đến remote MX trên port 25.
Gmail đánh dấu tất cả là spam hoặc trả lại mail. Mở message trong Gmail, chọn "Show original" rồi đọc kết quả xác thực. dkim=fail hoặc dkim=none nghĩa là bản ghi TXT dkim._domainkey bị thiếu, sai nội dung hoặc chưa kịp propagate. Publish lại chính xác nội dung mà trang ARC/DKIM hiển thị rồi chờ TTL hết hạn. spf=fail nghĩa là các bản ghi SPF/MX không bao phủ IP của bạn. Alignment là yếu tố quyết định; chỉ cần một lần kiểm tra fail là mail có thể vào spam.
Bị các nhà cung cấp lớn từ chối ngay khi kết nối. Bounce hoặc log Postfix có thể chứa lỗi PTR rejection của Gmail:
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.Mã 550 5.7.25 nghĩa là reverse DNS bị thiếu hoặc không khớp. Đặt PTR của IP thành mail.example.com tại nhà cung cấp (Bước 2). Forward (A) và reverse (PTR) phải khớp nhau, đồng thời cả hai phải trỏ đến cùng hostname mà mailcow dùng để giới thiệu với các server khác.
Trình duyệt hiển thị cảnh báo certificate và không bao giờ hết. Container acme-mailcow không lấy được certificate hợp lệ. Kiểm tra log của container:
sudo docker compose logs acme-mailcow | tail -n 40Dòng như Cannot validate any hostnames, skipping Let's Encrypt for 1 hour. hoặc lỗi challenge nghĩa là port 80 không thể truy cập từ Internet, hoặc bản ghi A không trỏ đến server này. Xác nhận mail.example.com phân giải đến máy chủ, mở port 80 và 443 trên mọi host firewall liên quan, đồng thời bảo đảm không có tiến trình nào khác bind vào các port đó. Sau khi sửa nguyên nhân, restart client bằng sudo docker compose restart acme-mailcow thay vì chờ hết thời gian back-off kéo dài một giờ.
FAQ
Email tự host có thực sự đáng không?
Nếu bạn muốn tự sở hữu dữ liệu, dùng alias không giới hạn và toàn quyền kiểm soát, thì có. mailcow cung cấp một stack chuyên nghiệp với chi phí tương đương một VPS. Tuy nhiên, duy trì khả năng gửi mail đến inbox luôn là công việc liên tục: danh tiếng IP, căn chỉnh DNS và giám sát blocklist không bao giờ hoàn tất hẳn. Với địa chỉ email doanh nghiệp quan trọng, nơi chỉ một ngày nằm trong thư mục spam của người khác cũng gây thiệt hại, dùng managed provider là lựa chọn thực tế hơn. Hãy tự host khi bạn coi trọng quyền kiểm soát hơn sự tiện lợi và thực sự sẵn sàng vận hành nó. Nếu mục tiêu thật sự của bạn là giữ dữ liệu ngoài server của người khác chứ không nhất thiết phải tự host email, hãy bắt đầu với một dịch vụ không gặp vấn đề về khả năng gửi mail: thư viện ảnh hoàn toàn không có vấn đề này, và bài so sánh PhotoPrism và Immich của chúng tôi trình bày yêu cầu RAM tối thiểu cùng các lệnh backup cho mô hình chạy trên loại VPS tương tự.
Làm sao biết outbound port 25 có bị block không?
Chạy nc -vz -w 5 gmail-smtp-in.l.google.com 25 trên server. "succeeded!" nghĩa là port đang mở; timed out sau thời gian chờ nghĩa là provider đã block port này. Đây là lý do phổ biến nhất khiến server tự host có thể nhận mail nhưng không gửi được mail. Cách khắc phục duy nhất là yêu cầu provider mở port; không có thay đổi cài đặt local nào xử lý được việc này.
Vì sao mail của tôi vẫn vào thư mục spam của Gmail?
Gần như luôn là do chuỗi xác thực bị lỗi. Dùng "Show original" trong Gmail và tìm spf=pass, dkim=pass và dmarc=pass. dkim=fail cho biết bản ghi TXT dkim._domainkey bị thiếu hoặc sai; PTR không khớp hoặc IP mới chưa có lịch sử gửi mail cũng làm giảm khả năng gửi đến inbox. Trước tiên hãy đưa điểm mail-tester.com lên 10/10, sau đó warm up IP từ từ: bắt đầu với vài message mỗi ngày rồi tăng dần, thay vì gửi một lượng lớn ngay trong ngày đầu.
Cụ thể tôi cần backup những gì?
Chạy backup_and_restore.sh backup all và lưu toàn bộ bộ backup bên ngoài server. Bộ này bao gồm vmail (các mailbox), crypt (các key dùng để giải mã mailbox), database MariaDB (domain, user, alias và setting), Redis, dữ liệu đã học của Rspamd và Postfix queue. Volume crypt là phần thường bị bỏ sót. Không có volume này, bản backup vmail chỉ là ciphertext không thể đọc được. Hãy test restore lên một máy tạm ít nhất một lần.
Tôi có thể chạy mailcow trên VPS 2 GB không?
Không nên nếu muốn vận hành ổn định. generate_config.sh đề nghị tắt ClamAV khi RAM dưới khoảng 2.5 GiB. Ngay cả khi đó, Rspamd, ClamAV, Dovecot và MariaDB vẫn tranh chấp memory, nên hệ thống sẽ dùng swap và gặp OOM kill khi có tải thực tế. Hãy xem 6 GiB RAM cộng 1 GiB swap là mức tối thiểu cho một cài đặt single-user ổn định. Chuyển lên 8 GiB ngay khi có hơn một vài người phụ thuộc vào hệ thống.