メインコンテンツへスキップ
50% off 全プラン対象、期間限定。月額 $2.48/mo
14 min left
Webアプリとビジネスアプリ

Listmonkでニュースレターを自前運用する方法

C 著者 Chike 14 分で読めます
How to self-host your newsletter with Listmonk on a VPS: Docker Compose, SMTP relay, and SPF/DKIM/DMARC deliverability.

自前で運用すれば、Listmonkはすでに料金を払っているVPS上で動きます。送信コストはSMTPリレーがメール1,000通あたりに課金する金額だけです。購読者数はどちらの金額も変えません。これこそが、マネージド型の無料枠を使い切ったときに、セットアップにかける時間を見合うものにする構造的な転換です。

Listmonkはオープンソースで、Go製のニュースレター管理ツールです。VPSとSMTPリレーアカウントの費用だけで、購読者、リスト、キャンペーンを無制限に扱えます。コマンドを打ち込む前に1つだけはっきりさせておくべきことがあります。Listmonkは実際の送信を除くすべてを担います。メールが受信トレイに届くか迷惑メールフォルダに入るかは、あなたが設定するSMTPリレーと、送信ドメインに設定するDNSレコードで決まります。

このガイドで扱う内容

  • ListmonkとPostgreSQLをDocker Composeでデプロイし、HTTPS対応のNginx(またはCaddy)リバースプロキシの背後に配置する
  • 送信量と予算に合ったSMTPリレーを選ぶ(Amazon SES、Postmark、Brevo、その他)
  • 送信ドメインにSPF、DKIM、DMARCを設定する
  • 明確なエラーとして表面化しないことが多い、本番環境の4つの障害パターンを回避する
  • 所要時間の目安。VPSとドメインが用意できていれば30分
  • 対象外。ドリップ自動化、トランザクションメール、複数インスタンス構成(FAQを参照)

Listmonkが適さない場面

Listmonkは特定の状況にこそ最適な答えです。状況が違えば、もっと良い答えがあります。

月間およそ10,000通未満の送信量。 この規模では、BrevoやMailchimpのマネージド無料枠のほうが、VPSとSMTPリレーを合わせた総額より安く済む場合があります。自前運用が割に合い始めるのは、その範囲を超えてからです。デプロイする前に、実際の購読者数と送信頻度に照らして数字を確認しましょう。

技術者がいないチーム。 MailchimpとBrevoは、ターミナルを使わない人にとって本当に使いやすいUIを備えています。Listmonkは、チームの誰かがサーバーにSSHでログインし、Dockerのログを読み、DNS伝播を解釈できることを前提にしています。そういう人がいないなら、マネージドサービスが正しい選択です。

自動化ワークフローが必要な場合。 Listmonkはキャンペーンを送信します。ドリップシーケンス、行動トリガー型メール、ビジュアルなワークフロービルダーには対応していません。それらが必要なら、 Mautic を使うか、自動化レイヤーとしてListmonkをn8nに連携させましょう。

GDPRの対象となる購読者リスト。 購読者が主にEU圏にいる、またはリストがGDPRのデータ所在地ルールの対象である場合は、Listmonkをヨーロッパのデータセンターで運用しましょう。当社はEUの所在地要件を満たすフランクフルトとロンドンのロケーションを提供しています。

始める前に必要なもの

Listmonkに加えてPostgreSQL、さらに中程度のキュー処理を動かすには、最低でも2 GBのRAMが必要です。本番環境で余裕を持たせるなら4 GBが目安です。

ハードウェア。 月間50,000通未満の個人向けリストなら、2 vCPU、4 GBのRAM、120 GBのNVMeストレージを備えたVPSで十分です。月間200,000通以上に成長するリストには、4 vCPUと8 GBのRAMが必要です。当社はこのCompose構成をフランクフルトの4 GB VPSで運用しています。可能なら購読者に近いロケーションを選びましょう。送信のレイテンシはさほど問題になりませんが、管理パネルの反応速度は影響します。

ドメイン。 AレコードであなたのVPSに向けたドメイン。管理画面用にはサブドメインを使いましょう。たとえば mail.example.comです。送信ドメインと管理用サブドメインは同じルートドメインでも構いません。

SMTPリレーアカウント。 まだ作成しないでください。リレーの選択はこのガイドで最も重要な決定であり、送信量によって変わります。「SMTPリレーを選ぶ」のセクションまで読み進めてプロバイダーを選び、SMTPのホスト、ポート、ユーザー名、パスワードを手元に用意してからここに戻ってきてください。

VPS上のソフトウェア。 Ubuntu 22.04 LTSまたは24.04 LTS。Docker Composeプラグインを備えたDocker Engine 24.0以上。ポート22、80、443を開放したUFWまたは同等のファイアウォール。非rootのsudoユーザーとしてのSSHアクセス。

ListmonkをDocker Composeでデプロイする

Listmonk and PostgreSQL deployed with Docker Compose on a VPS, behind an Nginx or Caddy reverse proxy that terminates HTTPS.

デプロイ用のディレクトリを作成し、そこに docker-compose.yml 2つのサービスを記述したファイルを置きます。データベース用のpostgresと、アプリケーション用のlistmonkです。どちらも障害時に再起動します。Listmonkは 127.0.0.1 にバインドするため、リバースプロキシだけが到達できます。

Docker Composeファイル

こちらが docker-compose.yml。正確なイメージタグと環境変数名は、 Listmonk公式インストールドキュメントと照らし合わせて確認してください。リリースごとに更新されます。

# docker-compose.yml
services:
  postgres:
    image: postgres:16-alpine
    container_name: listmonk-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: listmonk
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: listmonk
    volumes:
      - listmonk-postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U listmonk"]
      interval: 10s
      timeout: 5s
      retries: 6

  app:
    image: listmonk/listmonk:latest
    container_name: listmonk-app
    restart: unless-stopped
    # Bind to loopback only. The reverse proxy is the public entrypoint.
    ports:
      - "127.0.0.1:9000:9000"
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      LISTMONK_app__address: "0.0.0.0:9000"
      LISTMONK_db__host: postgres
      LISTMONK_db__port: 5432
      LISTMONK_db__user: listmonk
      LISTMONK_db__password: ${POSTGRES_PASSWORD}
      LISTMONK_db__database: listmonk

volumes:
  listmonk-postgres:

作成 .env ファイル付き POSTGRES_PASSWORD= を長いランダムな文字列に設定します。次にスタックを起動し、一度きりのデータベースインストールを実行します。

# Pull images and start the database first
docker compose up -d postgres

# Run the install step (creates schema and the first admin user)
docker compose run --rm app ./listmonk --install --idempotent --yes

# Start the application
docker compose up -d

現在の --install コマンドが管理者のメールアドレスとパスワードを尋ねてきます。これらを保存してください。両方のコンテナが稼働していることを確認します。

docker compose ps

期待される出力。2つのサービスが表示され、どちらもUp状態であること。postgresの行には(healthy)が表示されているはずです。

現在の 127.0.0.1:9000 へのバインドは意図的なものです。Listmonkには認証のレート制限機能もIPの許可リスト機能もありません。ポート9000を公開インターネットにさらすということは、世界中の誰もがあなたの管理ログインにアクセスできるということです。そのログインをHTTPS経由でのみ到達可能にするのがリバースプロキシです。

NginxリバースプロキシとSSL

UbuntuのリポジトリからNginxとCertbotをインストールします。次の場所にサイト設定を作成します。 /etc/nginx/sites-available/listmonk Listmonkが正しいキャンペーンリンクを生成するために必要なプロキシヘッダーを含めます。

# /etc/nginx/sites-available/listmonk
server {
    listen 80;
    server_name mail.example.com;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Listmonk streams campaign progress over WebSocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

これをsites-enabledにシンボリックリンクし、設定をテストしてNginxをリロードし、証明書を発行します。

sudo ln -s /etc/nginx/sites-available/listmonk /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d mail.example.com

Certbotはサーバーブロックを書き換えて新しい証明書で443をリッスンするようにし、HTTPからHTTPSへのリダイレクトを追加します。確認します。

curl -I https://mail.example.com

期待される出力。HTTP/2 200と、有効なstrict-transport-securityヘッダー。リダイレクトループが発生する場合は、上記のNginx設定で X-Forwarded-Proto ヘッダーが設定されているか確認してください。十中八九、ループの原因はそのヘッダーです。

このVPS上で動かすのがListmonkだけなら、代わりにCaddyを使いましょう。Caddyfileは3行で済み、cronジョブなしで証明書の更新を処理します。

mail.example.com {
    reverse_proxy 127.0.0.1:9000
}

Message-IDヘッダーを修正する

デフォルトでは、Listmonkは送信するMessage-IDヘッダーにシステムのホスト名を使います。VPSのホスト名がlocalhostや、有効なFQDNでない何かである場合、Listmonkは Message-ID: <[email protected]>を送信します。GmailやOutlookの迷惑メールフィルターはこれを即座に検出します。これは次に記載されています。 Cloudron Forumのスレッド15410.

修正はListmonkの config.tomlの1行です。新規インストールの場合は、次でファイルを生成します。 docker compose run --rm app ./listmonk --new-config。次にこう設定します。

[app]
hostname = "mail.example.com"

編集後にアプリのコンテナを再起動します。

docker compose restart app

これはキャンペーンを1通でも送信する前に行ってください。汚染された localhost.localdomain Message-IDを含むリストは、最初からクリーンだったリストよりも回復が困難です。

Pro Tip

If you would prefer to skip the Compose setup, check out our ワンクリックListmonk VPS をご覧ください。数分でワンクリックでListmonkをデプロイできます。インスタンスにはPostgreSQLがあらかじめ構成されています。それでもSMTPリレーの設定とDNSレコードの追加は必要です。どのようにデプロイしても、これらの手順は省略できません。

SMTPリレーを選ぶ

Choosing an SMTP relay for Listmonk: Amazon SES, Postmark, Brevo, and Mailgun compared by cost structure, bounce webhooks, and best fit.

すべての送信は、あなたが設定するリレーを経由します。リレーのIPレピュテーション、レート制限、バウンス処理が、メールが受信トレイに届くか迷惑メールフォルダに入るかを決めます。

以下が機能面の比較です。料金と無料枠の上限は変わります。契約する前に、各プロバイダーの公式料金ページで確認してください。

プロバイダーコスト構造バウンスのwebhook最適
Amazon SES従量課金、大量送信時は非常に低コストあり、SNS経由大量送信時のコスト。すでにAWSを利用している
Postmark月額基本料金に加えて従量課金あり、ネイティブ対応到達性重視。トランザクションメールでの評価
Brevo少量向けの無料枠、それ以上は有料プランありアップグレードの道がある少量向け
Mailgun従量課金ネイティブのwebhookエンドポイントなし。必要なら汎用のバウンスAPIを使う。開発者にとってなじみがある

以上は各SMTPリレーをざっと見たものです。続いて、それぞれを詳しく見ていきます。

SESは大量送信時に最も安価な選択肢であり、Listmonkコミュニティで最もよく話題になります。セットアップの手順はPostmarkやBrevoより多いものの、実際に量が出るとメール1通あたりのコスト差が大きく、その手間に見合います。

セットアップは3段階です。まず、次のポリシーを付与したIAMユーザーを作成します。 AmazonSESFullAccess ポリシー(または ses:SendRawEmailses:GetSendQuotaだけに絞ったより厳格なカスタムポリシー)。次に、SESコンソールで送信ドメインを検証します。SESが追加すべきDKIMのCNAMEを案内してくれます。3番目に、SESのSMTP設定パネルからSMTP認証情報を生成します。これらはAWSのアクセスキーではありません。「Create SMTP credentials」をクリックすると、SESがSMTP専用の別のユーザー名とパスワードを生成します。

Listmonkの管理画面でSettings → SMTPを開き、次の設定で新しいサーバーを追加します。

  • ホスト: email-smtp.<region>.amazonaws.com (ドメインを検証したSESのリージョンを使う)
  • ポート。587
  • 認証プロトコル。LOGIN
  • TLS。STARTTLS
  • ユーザー名とパスワード。SESが生成したSMTP認証情報

SESはポート587でSTARTTLSを必須としています。 TLSをnoneのままにしたり、ポート465を選んだりすると、Listmonkは接続し、SESは 530 Must issue a STARTTLS command firstを返しますが、管理パネルのSMTP認証情報テストはそれでも成功と表示することがあります。どのキャンペーンを実行する前にも、自分が管理している個人の受信トレイに本物のテストメールを送ってください。

新しいSESアカウントはサンドボックスモードで始まります。サンドボックスでは検証済みのメールアドレスにしか送信できず、購読者リストには使えません。SESコンソールからサポートチケットを開き、本番アクセスを申請してください。承認は通常1営業日で完了します。

Postmark(到達性重視の代替案)

PostmarkはSESよりメール1通あたりのコストは高いものの、ネイティブのバウンスwebhookに対応し、厳格な送信者ポリシーで高い受信トレイ到達率の評判があります。ニュースレターが事業上不可欠な場合や、SESのサンドボックスから本番への承認を管理したくない場合に、その価値があります。

Listmonkの設定はSESと同じ形です。ホスト、ポート587、STARTTLS、そしてPostmarkサーバーのAPIトークンパネルから取得した認証情報。Postmarkの署名設定で送信ドメインを検証し、Postmarkが生成するDKIMレコードを追加すれば、送信の準備は完了です。

メール1通あたりのコストよりも到達性が重要ならPostmarkを選びましょう。コストよりも送信量が重要で、手取り足取りの案内が不要ならSESを選びましょう。

SMTP認証情報テストに関する注意。 Listmonkの管理画面の接続テストは、認証情報が無効でも常に成功と報告します。これは いくつかのGitHub issueに記載されています。これを信用しないでください。どのリレーを設定した後でも、テスト用の購読者1人にキャンペーンを送り、リスト全体に送る前に対象の受信トレイで受信を確認してください。

大量のキャンペーン送信にMailersendは避けましょう。1接続あたり5通という上限があり、次のエラーが発生します。 421 Service not available Listmonkは配信が失敗しても送信済みとして記録するエラーです。Listmonk上ではキャンペーンが成功したように見え、警告なくメッセージの大半を取りこぼします。

メールを確実に届ける。SPF、DKIM、DMARC

SPF, DKIM, and DMARC DNS records on the sending domain that let receiving mail servers verify Listmonk's email and keep it out of spam.

これらは送信ドメインに設定する3つのDNSレコードで、あなたのドメインがこのリレーに代理送信を許可したことを受信メールサーバーに伝えます。どれか1つでも省くと、リレーや本文がどれだけクリーンでも、大量送信時には相当な割合が迷惑メールに入ります。最初のキャンペーンを送る前に、DNSプロバイダーで設定してください。

SPFレコード

SPFは、特定のIPや送信サービスがあなたのドメインのメールを送信することを認可します。送信ドメインのルートに、リレー用のincludeを記載したTXTレコードを1つ追加します。SESの場合、レコードは次のようになります。

v=spf1 include:amazonses.com ~all

Postmarkの場合は、includeを次に置き換えます。 include:spf.mtasv.net。正確なincludeの値は、必ずリレーの公式SPFドキュメントで確認してください。プロバイダーによって、時にはリージョンによっても変わります。

1つのドメインにSPFレコードは1つしか持てません。別のサービス(Google Workspace、Microsoft 365)向けにすでに1つある場合は、2つ目を追加するのではなく、既存のレコードにincludeを統合してください。

DKIM

DKIMは送信メールに暗号署名を付与し、受信サーバーがあなたのDNS内の公開鍵と照合して検証します。鍵ペアはリレーが生成します。あなたは公開鍵を、セレクターのサブドメイン(たとえば sel1._domainkey.example.com)にTXTレコードとして、リレーが提示する正確な値で追加します。

ListmonkはDKIM署名を扱いません。リレーが行います。Listmonk固有のDKIM設定はありません。リレーのDKIM設定ウィザードに従い、提示されたレコードを追加し、DNSの伝播を待ちます(通常は30分未満、ときに数時間)。

DMARC

DMARCは、SPFまたはDKIMのチェックに失敗したメールを受信サーバーがどう扱うかを指示します。まずは次の監視モードで始めます。 p=none 設定ミスを修正する間、到達性に影響を与えずに集計レポートで失敗を確認できるようにします。次の場所にTXTレコードを追加します。 _dmarc.example.com:

v=DMARC1; p=none; rua=mailto:[email protected]

クリーンなレポートが2、3週間続いたら、ポリシーを次に厳格化します。 p=quarantine or p=reject。監視フェーズを飛ばさないでください。SPFのincludeのタイプミスと、 p=reject 初日からの組み合わせは、何かが間違っているという合図もないまま、あなた自身の正当なメールを消し去ります。

List-Unsubscribeヘッダー(RFC 8058)はListmonkが自動的に生成します。Settings → Generalで有効になっていることを確認してください。GmailとApple Mailはこのヘッダーをワンクリックの配信停止オプションとして表示し、これが送信者のレピュテーションを守ります。

本番環境で実際に壊れるもの

最初の本物のキャンペーンを送るまで現れない4つの障害パターン。購読者が気づく前に見つけましょう。

問題1。バウンス率がリレーの数字と一致しない。 Listmonkは、指定したバウンス用メールアドレスをPOP3で読み取り、読んだメッセージをすべて削除することでバウンスを処理します。これには休暇の返信、配信受領通知、不在通知が含まれ、すべてバウンスとして分類されます。リレーは受信側メールサーバーが返した本物の配信失敗だけを数えます。SESが0.6%と報告し、Listmonkが4%と報告するなら、 これがその差です。修正方法は、POP3の代わりにバウンスwebhookのコールバックを設定することです。SESの場合はSNSを使ってバウンス通知をListmonkのwebhookエンドポイントに配信します。Postmarkの場合は、そのネイティブwebhookを同じエンドポイントに向けます。webhookによるバウンスは正確で、POP3によるバウンスは数を膨らませます。

問題2。SMTP認証情報テストが、間違っているのに成功と言う。 リレーのセクションで述べたとおり、接続テストは認証情報の有効性にかかわらず常に成功と報告します。これを信用しないでください。SMTP設定を変更したり設定したりしたら、必ず本物のテストメールを送ってください。

問題3。キャンペーンがエラーなしで送信途中で止まる。 Listmonkは、購読者の60%しかメールを受信していなくても、キャンペーンをFinishedとマークします。残りの送信はリレーに拒否されたか、VPSのネットワーク層でスロットリングされたもので、Listmonkはそのどちらもキャンペーンレベルのエラーとして表面化しません(Cloudron Forumのスレッド13165)。キャンペーンの送信数が購読者数より少ない場合は、その送信時間帯についてリレーのダッシュボードを開き、リレーが受け付けた件数とListmonkの件数を比較してください。真実はリレー側にあります。

問題4。誰もPostgreSQLをバックアップしていない。 Composeのボリュームは再起動をまたいでデータを保持します。しかし、ホストの障害、誤ったdocker volume rm、破損したアップグレードからは守ってくれません。日次のpg_dumpを追加しましょう。

0 2 * * * docker exec listmonk-postgres pg_dump -U listmonk listmonk > /backups/listmonk-$(date +\%Y\%m\%d).sql

まずこの行を手作業で1回実行してください。cronエントリーを信頼する前に、出力ファイルが空でないことを確認します。エラーを出さずにゼロバイトのファイルを書き込むバックアップスクリプトは、バックアップが無いよりも悪いものです。なぜなら、あなたがそのことを考えなくなるからです。

本番環境でこのどれかを信頼する前に、購読者1人にテストキャンペーンを送り、対象の受信トレイで受信を確認してください。その1通がきれいに届けば、次の10,000通も届きます。

よくある質問

Listmonkのバウンス率がAmazon SESの報告より高いのはなぜですか?

ListmonkのPOP3バウンス処理は、不在通知や休暇の自動応答をバウンスとして読み取るため、件数を膨らませます。正確な件数を得るには、SESのSNS webhookコールバックを設定してください。

Listmonkはトランザクションメールに対応していますか?

Listmonkはニュースレターと一斉キャンペーンのツールです。トランザクションメール(パスワードリセット、注文確認、1対1のトリガー型メール)はネイティブには扱いません。同じ送信ドメインからトランザクションメールを送るには、リレーのトランザクション用エンドポイントを別途設定するか、PostalやPostmarkのトランザクションAPIのような専用ツールをListmonkと併用してください。

Mailchimpの購読者をListmonkにインポートするにはどうすればよいですか?

MailchimpのリストをAudience → Export AudienceからCSVとしてエクスポートします。Listmonkで、Subscribers → Importに移動してCSVをアップロードします。プロンプトが表示されたら、メールと名前の列を対応付けます。Listmonkは、Mailchimp、ConvertKit、そしてほとんどのニュースレタープラットフォームの標準的なCSVエクスポートを、形式変換なしで受け付けます。

誰かがListmonkのキャンペーンを配信停止するとどうなりますか?

Listmonkはデフォルトで、すべてのキャンペーンメールに配信停止リンクを追加します。購読者がそれをクリックすると、ブロックリストに追加され、今後のすべてのキャンペーンから除外されます。List-Unsubscribeヘッダー(RFC 8058)が自動的に含まれるため、ワンクリックの配信停止に対応するメールクライアント(Gmail、Apple Mail)はそれをネイティブに表示します。購読者のレコードは監査目的でデータベースに残りますが、それ以降のキャンペーンが送られることはありません。

Share

ブログの他の記事

読み進める。

デプロイの準備はできましたか? 月額2.48ドルから。

2008年から独立運営のクラウド。AMD EPYC、NVMe、40 Gbps。14日間返金保証。