SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor

mailcow VPS 自架郵件:讓 Gmail 接受你的信件

安裝 mailcow 前先測試對外 TCP 25 埠,遭封鎖會讓郵件在佇列中最多停留 5 天後退回;正確設定 PTR,避免 Gmail 550 5.7.25 拒收。

你要建置的內容

在自己擁有的主機上建置完整郵件伺服器:使用 SMTP 傳送與接收郵件、使用 IMAP 讓手機與筆電保持同步、提供網頁郵件用戶端,以及對雙向郵件逐封評分的垃圾郵件篩選器。mailcow-dockerized 會將 Postfix、Dovecot、Rspamd、SOGo webmail、MariaDB、Redis 與 ACME client 整合到同一個 Docker Compose stack 中,因此軟體本身不是難點。你可以在半小時內讓它執行。

真正困難的是周邊條件。對電子郵件而言,網際網路上的其他郵件系統會主動不信任全新的伺服器;從「可以運作」到「Gmail 默默吞掉所有郵件」之間,主要取決於 4 筆 DNS 記錄,以及一項你可能無法完全控制的 IP reputation 設定。租用任何主機前,先閱讀以下先決條件。如果讀完後認為建立信譽的過程不值得,這也是合理的選擇;我們整理了 2026 年實際值得自行託管的服務,基於這些原因,會將電子郵件列為「只有確定有需求時才自行託管」。

先備條件就是整個專案

只要漏掉其中任何一項,就會寄出永遠無法送達的郵件。以下大致依各項導致問題的常見程度排序:

對外連出的 25 埠必須開放。 您的伺服器會透過 TCP 25 埠將郵件傳送至 Gmail 和 Microsoft。許多 VPS 與雲端服務供應商為了抑制垃圾郵件,預設會封鎖對外連出的 25 埠,而且通常不會顯示明顯錯誤。系統開機時一切看似正常,但郵件會持續在佇列中延遲,直到 Postfix 放棄並退回郵件(mailcow 將佇列最長存留時間設為 5 天)。安裝任何軟體前先進行測試。若遭到封鎖,唯一的處理方式是提交支援請求,要求供應商解除封鎖;有些供應商會對使用時間較長的帳戶開放,有些則永遠不會開放。

乾淨且具備可用信譽的 IP。 回收再利用的 VPS IP 常因前一位租用者寄送垃圾郵件,早已列入封鎖清單。正式使用前,請透過 Spamhaus lookup 或 mxtoolbox 等服務檢查您的 IP。IP 一旦列入清單,就會遭到拒收,無法靠程式碼規避。

具備 DNS 控制權,且 PTR 記錄正確。 您需要在網域的 zone 中新增記錄,也需要將伺服器 IP 的反向 DNS(PTR)指向郵件主機名稱。PTR 幾乎不會設定在您的 DNS 控制台中,而是由 IP 的持有人管理,因此必須在 VPS 供應商的控制台中設定,或透過支援請求申請設定。

6 GiB RAM 和 2 vCPU 是較舒適的最低配置。 mailcow 對私人安裝設定的最低需求是 6 GiB RAM 加上 1 GiB swap;當有少數幾位使用者依賴此服務時,建議使用 8 GiB。低於約 2.5 GiB 時,generate_config.sh 會提供停用 ClamAV 病毒掃描器的選項,避免 kernel 開始終止容器。mailcow 文件所列的磁碟最低需求是 20 GiB,且這還未包含任何郵件的儲存空間,因此起始配置應高於此數值,並預留信箱成長所需的空間。

使用 DNS 名稱,而不是單獨的 IP。 請選擇類似 mail.example.com 的主機名稱。這個名稱會同時成為您的 MAILCOW_HOSTNAME、TLS 憑證的主體、PTR 目標及 SMTP banner。所有設定都必須保持一致。

步驟 1:確認對外連線的 25 埠已開放

先執行這項測試。如果測試失敗,後續所有操作都沒有意義。在全新的 VPS 上,嘗試與實際的郵件伺服器建立 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!

遭封鎖的埠會完整等待 5 秒,然後失敗:

nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progress

在沒有自行設定對外防火牆規則的全新 VPS 上,這項逾時表示遭到供應商封鎖。這是供應商端的網路篩選,不是本機防火牆,因此無法透過本機變更修正。請開立支援工單:「請為我的 VPS 開放對外 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。目前刻意保留 2 筆記錄未設定:DKIM 會由 mailcow 在步驟 6 產生,PTR 則需現在到供應商的控制台設定。

將 10.0.0.10 的 PTR(反向 DNS)設為 mail.example.com,且必須是 MAILCOW_HOSTNAME 的完整值。這是多數人最容易遺漏的單一記錄,大型供應商也會依此拒收郵件。如果控制台沒有 rDNS 欄位,請提交支援工單。

步驟 3:安裝 Docker

mailcow 需要 Docker Engine 24.0 或更新版本,以及 Docker Compose 2.0 或更新版本。Compose 外掛程式(docker compose)是最簡單的方式,但 mailcow 也支援獨立的 docker-compose 二進位檔。請使用 Docker 官方的便利安裝指令碼,不要使用 Ubuntu 的 docker.io 套件,因為該套件完全未提供 Compose 外掛程式:

curl -fsSL https://get.docker.com | sudo sh
sudo docker compose version

您應該會看到 Docker Compose version 行,顯示 v2.x 或 v5.x。Compose v5 於 2025 年發布,功能上與 v2 完全相同,兩者皆符合 mailcow 的需求。如果 docker compose version 輸出 docker: 'compose' is not a docker command,表示 Docker Engine 已安裝,但尚未安裝 Compose 外掛程式。請從 Docker 的套件庫安裝外掛程式、重新執行上述指令碼,或參閱我們的 Docker Compose 基礎指南;該指南會從 Docker 自有的 apt 套件庫同時設定兩者。

步驟 4,複製 mailcow 並產生設定

cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
umask
sudo ./generate_config.sh

確認 umask 輸出 0022,是依照 mailcow 自身的安裝步驟操作。該步驟會在複製前設定 umask 0022。mailcow 不會檢查這個值,而全新的 Ubuntu 24.04 root shell 已經會提供 0022。接著,指令碼會要求輸入最重要的項目:完整網域名稱。輸入 mail.example.com;該值必須與 A 記錄及 PTR 完全一致。指令碼會寫入 mailcow.conf,這是整個堆疊讀取的單一環境檔案。若需要變更 Web 埠(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 box

在低 RAM 主機上,SKIP_FTS=y 是另一個可調整的項目:mailcow 文件列出的第二個記憶體消耗來源是全文搜尋。略過這項功能只會停用 Webmail 中的本文搜尋。

除非主機上的其他服務已使用這些埠,否則請保留 HTTP_PORT=80 和 HTTPS_PORT=443。mailcow 內建的 ACME 用戶端預設使用 HTTP-01 challenge;若要取得憑證,必須讓網際網路可連線至 80 埠。(mailcow 2026-03 及後續版本也可透過 ACME_DNS_CHALLENGE=y 使用 DNS-01 challenge。此方式不需要 80 埠,但需要存取 DNS 主機的 API。)因此,簡易配置不會在同一台主機上執行 獨立的 nginx 加 Certbot 配置;mailcow 會在內部簽發並續期自己的 TLS 憑證,而第二個服務若占用 80/443,便會造成衝突。mailcow 也記載了替代配置:使用 HTTP_BIND=127.0.0.1 HTTP_PORT=8080 HTTPS_BIND=127.0.0.1 HTTPS_PORT=8443 將 Web listener 綁定至本機,在 80 和 443 上設定反向代理;若希望由本機 Certbot 管理憑證,則將 mailcow 指向這些憑證,並加入續期 hook。對郵件伺服器而言,這會增加需要維護的元件,因此任何需要公開 Web 前端的其他服務也適用相同的取捨:例如 將 Jellyfin 媒體庫重新套用 90 年代影音出租店外觀的 Halcyon,預期會在 443 上使用自己的反向代理,除非將 mailcow 移至該代理後方,否則應部署在不同主機上。例外是完全不要求公開 Web 埠的服務:自架的 RustDesk relay 只監聽自己的 21115 至 21119 範圍,因此只要還有足夠的頻寬餘裕,就能與 mailcow 共用同一台主機。

步驟 5:啟動服務堆疊並登入

sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps

拉取作業會下載約 20 多個映像檔,請等待幾分鐘。當 docker compose ps 顯示所有容器皆為 running(或 healthy)時,請在瀏覽器中開啟 https://mail.example.com/admin。預設管理員登入資訊為:使用者名稱 admin,密碼 moohoo。請立即在管理介面的 System → Configuration,接著選取 Access → Administrators 來變更密碼。若瀏覽器顯示 NET::ERR_CERT_AUTHORITY_INVALID 警告,表示 ACME 憑證尚未簽發。先參閱下方的 ACME 失敗說明,再判斷是否發生問題;開始的 1 到 2 分鐘使用自我簽署的暫用憑證是正常情況。

步驟 6,新增網域、信箱並發布 DKIM

在管理介面中開啟 E-Mail → Configuration,於 Domains 分頁按一下 Add domain,並輸入 example.com。接著在 Mailboxes 下按一下 Add mailbox,建立 you@example.com 並設定密碼。此信箱已可正常使用,也能透過 IMAP 存取。

接著設定 DKIM。前往 System → Configuration,然後選取 Options → ARC/DKIM keys。mailcow 可能已在新增網域時產生金鑰;如果尚未產生,請在此處產生金鑰,選取該網域,保留 selector dkim,選擇 2048-bit,然後按一下 Add。複製畫面顯示的完整 TXT 值,並發布為:

dkim._domainkey.example.com.  TXT  "v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqh...long-key...QAB"

mailcow 的 Domains 頁面有 DNS 按鈕,會列出預期的所有記錄,並以綠色勾號或紅色叉號標示目前實際發布的結果。請將其作為檢查清單,在測試郵件傳遞前確認每一列都顯示綠色。發布後 DKIM 列仍顯示紅色,通常表示金鑰被錯誤分割成多個 TXT 片段;2048-bit 金鑰的長度超過單一 TXT 字串的 255 字元限制,因此請將它貼上為一個邏輯值,再讓 DNS 主機代為分割成多個片段。

步驟 7,測試郵件遞送並追求 10/10

前往 mail-tester.com,複製頁面顯示的隨機地址,然後從新的信箱寄送郵件到該地址。請在 https://mail.example.com/SOGo 登入 SOGo webmail,並從該處寄出。接著按一下「Then check your score」。

目標是取得 10/10。常見扣分項目及其原因如下:

  • SPF 未對齊:你的 MX/SPF 記錄遺失,或未涵蓋寄件 IP。請重新檢查 SPF TXT。
  • DKIM 簽章驗證失敗:dkim._domainkey TXT 遺失、仍在傳播,或內容遭到破壞。這是最常見的遺漏項目。
  • 沒有 PTR/PTR 不相符:反向 DNS 無法解析至 mail.example.com。請向服務提供者修正。
  • 列入封鎖清單:你的 IP 過去的信譽不佳。請申請移除,或要求更換信譽較佳的 IP。

在結果顯示 10/10 之前,不要將實際郵件寄往 Gmail 或 Outlook。低分加上新 IP,可能導致你的網域在啟用第一天就被標記。

步驟 8:連線至實際的郵件用戶端

使用下列設定,將 Thunderbird、Apple Mail 或手機連線至伺服器。所有用戶端的伺服器主機都是 mail.example.com:

  • IMAP:連接埠 993,SSL/TLS(或使用 STARTTLS 的 143)
  • SMTP submission:連接埠 465,SSL/TLS(或使用 STARTTLS 的 587)
  • 使用者名稱:完整地址,you@example.com
  • 密碼:您設定的信箱密碼

絕不要透過連接埠 25 傳送用戶端郵件。該連接埠僅供伺服器對伺服器使用,mailcow 不會在此提供已驗證的 submission,因此連線至該連接埠的用戶端會遭拒。如果用戶端顯示 Relay access denied,表示它嘗試透過 25 傳送,或未進行驗證;請改用 465 或 587,並輸入信箱憑證。

步驟 9:備份真正重要的資料

mailcow 提供備份指令碼,會建立所有具狀態 volume 的快照。請將備份寫入外接磁碟或已掛載的遠端儲存空間:

sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \
  ./helper-scripts/backup_and_restore.sh backup all

all 會擷取 6 項資料。遺失其中任何一項都會造成資料遺失:vmail(實際的信箱)、crypt(解密 vmail 所需的金鑰,缺少 vmail 無法使用)、mysql(儲存網域、使用者、別名與設定的 MariaDB)、redis(佇列與快取狀態)、rspamd(已學習的垃圾郵件與正常郵件資料),以及 postfix(郵件佇列)。它會在輔助容器中執行並寫入壓縮封存檔,因此即使整個 stack 正在運作,備份仍能保持一致。請以每晚執行的 cron job 自動化,並加入 --delete-days 14 來刪除舊的備份組。停止運作的 cron job 不會主動顯示錯誤,因此請將指令碼的結束狀態傳送到你確實會注意到的位置;自架的 ntfy server 可在相同的 cron 項目中使用一行 curl,讓你在故障發生當晚於手機上收到通知。還原時使用相同的指令碼並加上 restore;此選項會列出快照,讓你選擇要還原的內容。從未實際測試還原過的備份只是希望,不是真正的備份。請先將備份執行一次 dry run,還原到測試用 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),因為資料庫結構遷移不易還原。更新發布頻繁,且包含面向網際網路之 daemon 的安全修正,因此不要讓郵件伺服器數月未更新。若更新導致某個容器狀態不正常,sudo docker compose logs --tail=50 <service>-mailcow 會指出未能重新啟動的 daemon。

強化設定說明

mailcow 會執行自己的 netfilter 服務(netfilter-mailcow),封鎖持續攻擊郵件與 webmail 埠的 IP,因此郵件服務預設已有防護。但這不包含主機本身的 SSH。SSH 仍對外公開,也仍會遭受暴力破解。請將此建置搭配 監控 SSH 驗證日誌的 Fail2ban,並僅允許使用金鑰登入。mailcow 管理介面應使用高強度密碼,理想情況下不要暴露在公用網際網路上,或改由 VPN 保護。

失敗情況與精確字串

郵件佇列中的郵件一直無法投遞。 執行 sudo docker compose exec postfix-mailcow postqueue -p,或查看管理介面的郵件佇列;項目會停留在 deferred 狀態,最長達 five days,之後 Postfix 會退回郵件,並顯示:

status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)

這幾乎總是因為服務供應商封鎖對外連線的 25 埠(步驟 1)。主機自身防火牆的對外規則也會產生相同的逾時,因此應先排除這項因素;若主機設定正常,便無法透過設定修正,請提交支援工單。問題不是 DNS,也不是 TLS;判斷依據是連往遠端 MX 的 25 埠連線中出現 timed out 一詞。

Gmail 將所有郵件標記為垃圾郵件,或退回郵件。 在 Gmail 中開啟郵件,選取「Show original」,然後查看驗證結果。dkim=fail 或 dkim=none 表示你的 dkim._domainkey TXT 記錄遺失、內容錯誤或尚未完成傳播;請完全依照 ARC/DKIM 頁面顯示的內容重新發布,並等待 TTL 到期。spf=fail 表示 SPF/MX 記錄未涵蓋你的 IP。對齊狀態是關鍵;只要有一項檢查失敗,郵件就可能進入垃圾郵件。

連線時遭大型服務供應商拒絕。 退信或 Postfix 日誌會包含 Gmail 的 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 遺失或不相符。請在服務供應商處將你的 IP 的 PTR 設為 mail.example.com(步驟 2)。正向 DNS(A)與反向 DNS(PTR)必須相符,且兩者都必須指向 mailcow 向其他伺服器宣告的同一個主機名稱。

瀏覽器持續顯示憑證警告。 acme-mailcow 容器未能取得有效憑證。請查看其日誌:

sudo docker compose logs acme-mailcow | tail -n 40

若出現類似 Cannot validate any hostnames, skipping Let's Encrypt for 1 hour. 的行,或挑戰驗證失敗,表示從網際網路無法連入 80 埠,或 A 記錄未指向此伺服器。若你發布了 AAAA 記錄,也請一併檢查:mailcow 會先依 AAAA 記錄進行驗證;只要存在 AAAA 記錄,就會略過 A 記錄檢查。因此,即使 A 記錄正確,錯誤的 AAAA 記錄或無法連入 80 埠的 IPv6 路徑仍會導致驗證失敗。確認 mail.example.com 解析至此主機,允許任何主機防火牆通過 80 與 443 埠,並確認沒有其他服務占用這些埠。修正原因後,使用 sudo docker compose restart acme-mailcow 重新啟動用戶端,不要等待長達一小時的退避時間結束。

FAQ

自架電子郵件真的值得嗎?

如果你重視資料所有權、無限別名與完整控制權,值得。mailcow 能以 VPS 的價格提供專業的郵件堆疊。但郵件投遞能力需要持續維護:IP 信譽、DNS 對齊與封鎖清單監控都沒有真正結束的一天。若這是攸關業務的電子郵件地址,而郵件進入他人垃圾郵件資料夾一天就會造成損失,託管服務商是較務實的選擇。當你重視控制權勝過便利性,且確實願意維護時,才適合自架。如果你真正想要的是讓資料離開他人的伺服器,而不一定是電子郵件,請先從不受他人決定的服務開始:相片資料庫完全沒有郵件投遞問題,而我們的 PhotoPrism 與 Immich 比較涵蓋了在同類 VPS 上建置時所需的最低 RAM 與備份指令。

如何知道對外的 25 埠是否遭到封鎖?

在伺服器上執行 nc -vz -w 5 gmail-smtp-in.l.google.com 25。顯示「succeeded!」表示連接埠已開放;等待後顯示 timed out,表示該連接埠遭到封鎖。若伺服器沒有自行設定的對外防火牆規則,封鎖來源就是服務商。這是自架伺服器能接收郵件卻始終無法寄出的最常見原因,而服務商封鎖只能由服務商開放該連接埠解決,任何本機設定都無法改變這點。

為什麼郵件仍會進入 Gmail 的垃圾郵件資料夾?

幾乎都是驗證鏈設定錯誤。請在 Gmail 中使用「Show original」,並尋找 spf=pass、dkim=pass 與 dmarc=pass。dkim=fail 表示 dkim._domainkey TXT 記錄遺失或內容錯誤;PTR 不相符,或使用沒有寄信歷史的新 IP,也會降低投遞能力。請先讓 mail-tester.com 達到 10/10,再逐步暖機 IP,每天先寄出少量郵件,逐漸增加,而不是在第一天就大量寄送。

我確切需要備份哪些內容?

執行 backup_and_restore.sh backup all,並將完整備份集存放在伺服器之外。它會擷取 vmail(信箱)、crypt(解密信箱所需的金鑰)、MariaDB 資料庫(網域、使用者、別名與設定)、Redis、Rspamd 的學習資料,以及 Postfix 佇列。crypt volume 最容易被忽略;沒有它,vmail 備份會是無法讀取的密文。至少在測試用伺服器上測試還原一次。

我可以在 2 GB VPS 上執行 mailcow 嗎?

不建議。generate_config.sh 會在低於約 2.5 GiB 時提供停用 ClamAV 的選項;即使停用 ClamAV,Rspamd、Dovecot、MariaDB 與其餘堆疊仍會競爭記憶體,因此只要有實際負載,就會使用 swap 並觸發 OOM kill。穩定的單一使用者安裝應將 6 GiB 加上 1 GiB swap 視為最低配置;只要有超過幾個人依賴這項服務,就應立即升級至 8 GiB。