mailcowをVPSで構築してGmailに届くメールサーバーを作る
mailcowを入れる前に送信方向のポート25を確認してください。ブロックされるとメールはキューに残ったままです。DNSを整え、Gmailの550 5.7.25 PTR拒否を防ぎます。
構築するもの
所有するサーバー上に、完全なメールサーバーを構築します。SMTP でメールを送受信し、IMAP でスマートフォンとノート PC の状態を同期します。Webmail クライアントと、すべてのメッセージを送受信の両方向でスコアリングするスパムフィルターも用意します。mailcow-dockerized は、Postfix、Dovecot、Rspamd、SOGo webmail、MariaDB、Redis、ACME client を 1 つの Docker Compose スタックにまとめます。そのため、ソフトウェア自体が難所になるわけではありません。30 分以内に稼働させられます。
難しいのは、その周辺にあるすべての作業です。インターネット上の他のサーバーが、新しく構築されたサーバーを積極的に信用しないサービスは、メールだけです。「動作する」状態と「Gmail がすべてのメッセージを黙って破棄する」状態の差は、4 つの DNS レコードと、完全には制御できない可能性がある 1 つの IP レピュテーション設定に集約されます。何かを借りる前に、以下の前提条件を確認してください。確認したうえで、レピュテーションを積み上げる作業に見合う価値がないと判断しても、それは正当な結論です。2026 年に実際に自己ホストする価値があるものの概要でも、まさにこの理由から、メールを「本気で取り組む場合に限る」項目として扱っています。
前提条件がプロジェクトそのものです
これらを1つでも欠かすと、送信したメールが届きません。以下は、問題の原因になりやすい順のおおまかな並びです。
送信方向のポート25を開放する必要があります。 サーバーはTCPポート25でGmailやMicrosoftにメールを配送します。スパム対策として、多くのVPSやクラウドプロバイダーは送信方向のポート25を初期状態でブロックしています。この制限は通知されません。起動時にエラーは発生せず、すべて正常に見える一方で、メールはキューに残ったままになります。何かをインストールする前にテストしてください。ブロックされている場合、唯一の対処方法は、プロバイダーに開放を依頼するサポートチケットを送ることです。利用期間の長いアカウントであれば開放するプロバイダーもありますが、開放しないプロバイダーもあります。
評判に問題のない、利用可能なIPを用意します。 再利用されたVPSのIPは、以前の利用者が送信したスパムによって、すでにブロックリストに登録されていることがあります。導入を決める前に、Spamhaus lookupやmxtoolboxなどのサービスで確認してください。IPが登録されていると、設定やコードでは解決できない拒否が発生します。
DNSを管理でき、正しいPTRレコードを設定できることが必要です。 ドメインのゾーンにレコードを追加し、サーバーの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ウイルススキャナーを無効にするよう提案します。開始時点で20 GBのSSDを割り当ててください。
IPアドレスだけでなく、DNS名を用意します。 mail.example.com のようなホスト名を選びます。この1つの名前を、MAILCOW_HOSTNAME、TLS証明書のサブジェクト、PTRの参照先、SMTPバナーに使用します。すべての場所で同じ名前を使ってください。
手順 1、外向きの port 25 が開いていることを確認する
最初に実施してください。ここで失敗すると、以降の作業はすべて無駄になります。新規 VPS から、実在する mail server への 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!port がブロックされている場合は、5 秒間待機した後に失敗します。
nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progressこのタイムアウトがブロックの兆候です。原因はプロバイダー側のネットワークフィルターであり、ローカルの firewall ではありません。そのため、ローカル設定を変更しても解決しません。次の内容でチケットを起票してください。
「VPS(<IP>)の外向き TCP port 25 を有効にしてください。正規の mail server を運用しています。」
この結果が "succeeded" になるまで、mailcow をインストールしないでください。受信方向の 25(他の server から自分の server への接続)は別経路で、通常は開いています。プロバイダーが制限するのは外向きの接続です。
ステップ 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 つあります。mailcow が Step 6 で生成する DKIM と、プロバイダーの管理画面で今設定する PTR です。
10.0.0.10 の PTR(逆引き DNS)を mail.example.com に設定します。値は MAILCOW_HOSTNAME と完全に一致させてください。これは最も忘れられやすいレコードであり、大手プロバイダーはこの設定がないと拒否します。管理画面に rDNS の入力欄がない場合は、チケットを発行して依頼してください。
Step 3, Docker のインストール
mailcow には、Compose v2 プラグインを備えた Docker Engine が必要です。Compose プラグインがまったく含まれていない Ubuntu の docker.io パッケージではなく、Docker 公式の簡易インストールスクリプトを使用します。
curl -fsSL https://get.docker.com | sudo sh
sudo docker compose versionDocker Compose version v2.x の行が表示されます。docker compose version が docker: 'compose' is not a docker command を出力する場合、Docker Engine はインストール済みですが、Compose プラグインがありません。Docker のリポジトリからプラグインをインストールするか、上記のスクリプトを再実行してください。または、Docker 独自の apt リポジトリから両方をセットアップする Docker Compose 基礎ガイド に従ってください。
Step 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 シェルでは、すでに 0022 になっています。次にスクリプトは、重要な項目を 1 つだけ尋ねます。完全修飾ホスト名です。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 boxSKIP_FTS=y は、RAM の少ないサーバーで調整できるもう 1 つの項目です。mailcow のドキュメントでは、全文検索を 2 番目にメモリを消費する機能としています。これを省略すると、Webmail で本文を検索できなくなります。
ホスト上の別のサービスがすでに使用している場合を除き、HTTP_PORT=80 と HTTPS_PORT=443 は変更しないでください。mailcow の組み込み ACME クライアントは、証明書を取得するためにインターネットから port 80 へ到達できる必要があります。そのため、同じサーバーで nginx と Certbot を別途構成する方法は使用しません。mailcow は内部で独自の TLS 証明書を発行・更新するため、port 80/443 を別のサービスが使用すると動作しません。公開 Web フロントエンドを必要とする他のサービスにも同じことが当てはまります。したがって、Jellyfin のライブラリを 90 年代のビデオ店風に変更する Halcyonのように、port 443 で独自のリバースプロキシを必要とする構成は、別のホストで運用します。例外は、公開 Web port をまったく要求しないサービスです。セルフホスト型の RustDesk relay は専用の 21115 から 21119 の範囲だけで待ち受けるため、十分な帯域に余裕があれば同じサーバーで共有できます。
ステップ 5: スタックを起動してログインする
sudo docker compose pull
sudo docker compose up -d
sudo docker compose pspull により、およそ 20 個のイメージが取得されます。数分待ってください。docker compose ps ですべてのコンテナが running(または healthy)と表示されたら、ブラウザーで https://mail.example.com を開きます。デフォルトの管理者ログインは、ユーザー名が admin、パスワードが moohoo です。Access → Administrators の管理画面で、直ちにパスワードを変更してください。ブラウザーに NET::ERR_CERT_AUTHORITY_INVALID の警告が表示された場合、ACME 証明書がまだ発行されていません。故障と判断する前に、下記の ACME の失敗に関する説明を確認してください。最初の 1〜2 分は自己署名の仮証明書が表示されるのが正常です。
ステップ 6: ドメイン、メールボックスを追加し、DKIM を公開する
管理 UI で Mail Setup ページ(Configuration → Mail Setup)を開き、Domains タブで Add domain をクリックして example.com を入力します。次に、Mailboxes で Add mailbox をクリックし、パスワードを設定して you@example.com を作成します。これで、IMAP からすでに接続できるメールボックスが完成します。
次に DKIM キーを設定します。Configuration → 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 のキーは、1 つの TXT 文字列に使用できる255文字の上限を超えます。そのため、1つの論理値として貼り付け、DNS ホストにチャンクへの分割を任せてください。
手順 7、配信テストを行い、10/10 を目指す
mail-tester.com にアクセスし、表示されたランダムなアドレスをコピーします。新しいメールボックスからそのアドレスへメッセージを送信します。https://mail.example.com/SOGo で SOGo Webmail にログインし、そこから送信してください。次に「Then check your score」をクリックします。
10/10 を目指します。よくある減点と原因は次のとおりです。
- SPF not aligned、
MX/SPF レコードがないか、送信元 IP が対象に含まれていません。SPF TXT レコードを再確認します。 - DKIM signature does not verify、
dkim._domainkeyTXT レコードがない、伝播中である、または内容が壊れています。最も多い見落としです。 - No PTR / PTR mismatch、逆引き DNS が
mail.example.comに解決されません。プロバイダー側で修正します。 - Listed on a blocklist、IP の過去の評価が原因です。ブロックリストからの削除を申請するか、よりクリーンな IP を依頼します。
10/10 と表示されるまで、Gmail や Outlook に実際のメールを送信しないでください。低いスコアと新しい IP の組み合わせは、初日からドメインがフラグ付けされる原因になります。
Step 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 には、すべてのステートフルなボリュームをスナップショットするバックアップスクリプトが付属しています。外付けディスク、またはマウント済みのリモートストレージに対して実行します。
sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \
./helper-scripts/backup_and_restore.sh backup allall は 6 つの対象を取得します。どれか 1 つを失うとデータも失われます。対象は、実際のメールボックスである vmail、vmail の復号に使う鍵を保持する crypt、ドメイン、ユーザー、エイリアス、設定を保持する MariaDB のデータベース mysql、キューとキャッシュの状態を保持する redis、学習済みのスパム・ham 判定を保持する rspamd、メールキューである postfix です。スクリプトは補助コンテナ内で実行され、圧縮アーカイブを書き出します。そのため、スタックの稼働中でもバックアップの整合性が保たれます。夜間の cron ジョブで自動化し、--delete-days 14 も追加して古いバックアップセットを削除します。動作しなくなった cron ジョブは何も通知せずに失敗するため、スクリプトの終了ステータスを、実際に確認できる場所へ送信してください。自己ホストの ntfy サーバーなら、同じ cron エントリから 1 行の curl を実行するだけで、失敗した夜にスマートフォンへ通知できます。restore を指定して同じスクリプトを実行すると復元できます。スナップショットの一覧が表示され、復元する対象を選択できます。一度もテスト復元していないバックアップは、バックアップではなく希望にすぎません。スクラッチ用の VPS に対して、まずドライランを 1 回実行してください。
手順 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)。スキーマの移行は元に戻すことが難しいためです。更新は頻繁に提供され、インターネットに公開するデーモン向けのセキュリティ修正も含まれます。そのため、メールサーバーを数か月更新しないままにしないでください。更新後にコンテナが unhealthy になった場合は、sudo docker compose logs --tail=50 <service>-mailcow で再起動できなかったデーモンを確認できます。
強化に関する注意
mailcow は独自の netfilter サービス(netfilter-mailcow)を実行し、メールポートと webmail ポートに対して大量の接続を試みる IP を遮断するため、メール側は標準状態で保護されます。ただし、ホスト自体の SSH は対象外です。SSH は引き続き公開され、ブルートフォース攻撃を受けます。この構成には、SSH の認証ログを監視する Fail2ban と鍵認証のみのログインを組み合わせてください。mailcow の管理 UI には強力なパスワードを設定し、できればパブリックインターネットから隔離するか、VPN 経由だけでアクセスできるようにしてください。
失敗パターンと、そのままの文字列
メールキューに入り、配信されない。 sudo docker compose exec postfix-mailcow postqueue -p を実行するか、管理 UI のメールキューを確認します。エントリが deferred のまま残り、次の内容が表示されます。
status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)これは、プロバイダーによって外向きの port 25 がブロックされている状態です(Step 1)。設定では解決できないため、サポートにチケットを作成してください。DNS や TLS の問題ではありません。remote MX の port 25 に対して timed out という語が表示されることが判断材料です。
Gmail ですべて迷惑メールに分類される、またはバウンスする。 Gmail でメッセージを開き、「Show original」を選択して認証結果を確認します。dkim=fail または dkim=none は、dkim._domainkey TXT が存在しない、壊れている、またはまだ伝播していないことを示します。ARC/DKIM ページに表示された内容をそのまま再公開し、TTL が切れるまで待ちます。spf=fail は、SPF/MX レコードが IP を対象にしていないことを示します。アライメントが重要です。1 つでもチェックに失敗すると、迷惑メールに分類される可能性があります。
接続時に大手プロバイダーから拒否される。 バウンスまたは 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 コードは、reverse DNS が存在しないか、一致していないことを示します。プロバイダー側で、IP の PTR を mail.example.com に設定します(Step 2)。Forward (A) と reverse (PTR) は一致している必要があります。また、どちらも mailcow が他のサーバーに対して名乗る同じホスト名を指定する必要があります。
ブラウザーに証明書警告が表示され続ける。 acme-mailcow コンテナが正規の証明書の取得に失敗しています。ログを確認します。
sudo docker compose logs acme-mailcow | tail -n 40Cannot validate any hostnames, skipping Let's Encrypt for 1 hour. のような行や challenge の失敗は、port 80 にインターネットから到達できないか、A レコードがこのサーバーを指していないことを示します。mail.example.com がこのサーバーを解決することを確認し、ホストファイアウォールで 80 と 443 を開放します。また、別のプロセスがこれらのポートを bind していないことを確認します。原因を修正したら、1 時間のバックオフが終わるのを待たずに sudo docker compose restart acme-mailcow でクライアントを再起動します。
FAQ
メールのセルフホスティングには本当に価値がありますか?
データの所有権、無制限のエイリアス、完全な制御が必要なら、価値があります。mailcow は VPS の料金で本格的なメール基盤を提供します。ただし、メールの到達性の維持は継続的な作業です。IP レピュテーション、DNS の整合性、ブロックリストの監視が終わることはありません。誰かの spam フォルダーに 1 日入るだけで損失が出るような業務上重要なアドレスには、マネージドプロバイダーが現実的です。利便性より制御を重視し、実際に保守できる場合にセルフホスティングを選んでください。メールそのものではなく、他者のサーバーからデータを移したいだけなら、まずは他者が決定権を持たないサービスから始めるとよいでしょう。フォトライブラリにはメールの到達性の問題がなく、同種の VPS で構築する場合に必要な RAM の下限とバックアップコマンドは、PhotoPrism と Immich の比較で説明しています。
送信ポート 25 がブロックされているかどうかは、どう確認できますか?
サーバー上で nc -vz -w 5 gmail-smtp-in.l.google.com 25 を実行します。「succeeded!」と表示されれば開放されています。待機後に timed out が表示される場合は、プロバイダーがブロックしています。これは、セルフホスティングのサーバーがメールを受信できても送信できない最も一般的な原因です。解決するにはプロバイダーにポートを開放してもらう必要があり、ローカル設定では変更できません。
メールがまだ Gmail の spam フォルダーに入るのはなぜですか?
ほとんどの場合、認証チェーンが壊れています。Gmail で「Show original」を開き、spf=pass、dkim=pass、dmarc=pass を確認します。dkim=fail がある場合、dkim._domainkey TXT レコードが存在しないか、内容が壊れている可能性があります。PTR の不一致や、送信履歴のない新しい IP も悪影響を与えます。まず mail-tester.com で 10/10 を取得し、その後は 1 日に数通から始めて徐々に増やし、初日から大量送信しないようにして IP をゆっくりウォームアップします。
具体的に何をバックアップする必要がありますか?
backup_and_restore.sh backup all を実行し、生成された一式をサーバー外に保管します。これには vmail(メールボックス)、crypt(メールボックスの復号に使う鍵)、MariaDB データベース(ドメイン、ユーザー、エイリアス、設定)、Redis、Rspamd の学習データ、Postfix キューが含まれます。crypt ボリュームは見落とされやすい項目です。これがなければ、vmail のバックアップは読めない暗号文になります。少なくとも 1 回は、検証用のマシンに復元してテストしてください。
2 GB の VPS で mailcow を実行できますか?
快適には実行できません。generate_config.sh は約 2.5 GiB 未満では ClamAV の無効化を提案しますが、それでも Rspamd、ClamAV、Dovecot、MariaDB がメモリを奪い合うため、実際の負荷がかかると swap と OOM kill が発生します。安定した単一ユーザー構成の下限は 6 GiB と 1 GiB の swap と考え、2 人を超えるユーザーが依存するようになった時点で 8 GiB に増やしてください。