SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-30

Mailcow VPS 自架郵件,讓 Gmail 接受寄信

安裝 mailcow 前先測試對外 port 25;若遭封鎖,郵件會永遠停在佇列。再設定 PTR,避免 Gmail 回傳 550 5.7.25 拒收。

建置目標

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

難點在於周邊環境。對電子郵件而言,網際網路上的其他服務會主動不信任全新的伺服器;「可以運作」與「Gmail 悄悄丟棄所有郵件」之間的差異,取決於 4 筆 DNS 記錄,以及 1 項你可能無法完全控制的 IP reputation 設定。租用任何資源前,請先閱讀下方的先決條件。如果讀完後決定不值得投入時間建立聲譽,這是合理的選擇;我們整理了 2026 年真正值得自行代管的服務,正是基於這些原因,將電子郵件歸類為「只有確定要這麼做時才自行代管」。

先備條件就是整個專案

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

對外連線的 port 25 必須開放。 你的伺服器會透過 TCP port 25 將郵件傳送至 Gmail 和 Microsoft。許多 VPS 和雲端服務供應商為了防堵垃圾郵件,預設會封鎖對外 port 25。這類封鎖通常不會顯示錯誤,開機時也不會有異常,一切看似正常,但郵件只會永遠停留在佇列中。請在安裝任何元件前先測試。如果遭到封鎖,唯一的處理方式是提交支援請求,要求供應商開放該埠;有些供應商會為使用一段時間的帳戶開放,有些則永遠不會開放。

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

具備 DNS 管理權限,且 PTR 記錄正確。 你需要在網域的 DNS 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 開始終止容器。起始時請配置 20 GB SSD 空間。

使用 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!

連接埠遭封鎖時,指令會完整等待五秒後失敗:

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

這個逾時表示連線遭到封鎖。這是供應商端的網路篩選,不是本機防火牆造成,因此修改本機設定無法解決。請提交支援請求:「Please enable outbound TCP port 25 for my VPS at <IP>; I am running a legitimate mail server.」在此檢查顯示 "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 搭配 Compose v2 外掛程式。請使用 Docker 的官方便利安裝指令碼,不要使用 Ubuntu 的 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 自己的 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 在檔案遮罩異常時會拒絕建置,而全新的 Ubuntu 24.04 root shell 已經提供 0022。接著,指令碼會詢問最重要的資訊:完整網域名稱。輸入 mail.example.com,其值必須與 A 記錄和 PTR 完全一致。指令碼會寫入 mailcow.conf,這是整個堆疊讀取的單一環境檔案。若需要變更 Web 埠(HTTP_PORTHTTPS_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

在低記憶體主機上,SKIP_FTS=y 是另一項可調整的設定:mailcow 文件指出,全文搜尋是第二個最耗用記憶體的功能。略過此功能只會使 Webmail 無法搜尋郵件本文。

除非主機上已有其他服務使用 HTTP_PORT=80HTTPS_PORT=443,否則請保留這兩個設定。mailcow 內建的 ACME 用戶端需要讓網際網路可連線至 80 埠,才能取得憑證。這也是不能在同一台主機上執行獨立的 nginx-plus-Certbot 設定的原因;mailcow 會在內部簽發並續期自己的 TLS,而第二個服務若占用 80/443,就會造成衝突。任何需要公開 Web 前端的其他服務也相同。因此,像將 Jellyfin 媒體庫重新設計成 90 年代錄影帶店的 Halcyon這類預期在 443 上使用自己的反向代理的建置,應放在另一台主機上。例外是完全不要求公開 Web 埠的服務:自架的 RustDesk relay只監聽自己的 21115 至 21119 埠範圍,因此只要還有足夠的頻寬餘裕,就能共用這台主機。

步驟 5:啟動堆疊並登入

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

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

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

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

接著設定 DKIM 金鑰。前往 Configuration → ARC/DKIM keys。mailcow 可能已在新增網域時產生金鑰;如果沒有,請在此處產生金鑰,選取該網域,保留選取器 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 狀態停留,並顯示:

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 中開啟郵件,選取「顯示原始郵件」,然後查看驗證結果。dkim=faildkim=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)。正向(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. 的行,或挑戰驗證失敗,表示網際網路無法連線至 80 埠,或 A 記錄未指向此伺服器。確認 mail.example.com 能解析至此主機,透過任何主機防火牆開放 80 與 443 埠,並確認沒有其他服務繫結這些埠。修正原因後,使用 sudo docker compose restart acme-mailcow 重新啟動用戶端,不要等待長達 1 小時的退避時間結束。

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=passdkim=passdmarc=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 的選項,但即使如此,Rspamd、ClamAV、Dovecot 與 MariaDB 仍會爭用記憶體,因此只要有實際負載,就可能使用 swap 並遭到 OOM 終止。對穩定的單一使用者安裝而言,應將 6 GiB 加上 1 GiB swap 視為最低配置;一旦有超過少數幾人依賴這項服務,就應立即升級至 8 GiB。