SSD Nodes Learn メモリ 8GB — 年額 $66
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-01

ListmonkをUbuntu 24.04に構築する方法

Listmonk v6.2.0をUbuntu 24.04へ導入します。PostgreSQL、config.toml、systemd、TLS、SMTPの設定と、配信レピュテーション確立に数週間かかる理由を解説します。

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

Listmonkでセルフホスト型ニュースレターを運用するために必要なもの

Listmonkは、セルフホスト型のニュースレターおよびメーリングリスト管理システムです。Goのバイナリ1つ、PostgreSQLデータベース1つ、設定ファイル1つ、systemdユニット1つで構成されます。Listmonkは購読者を保存し、キャンペーンをキューに登録しますが、メール自体は配信しないため、小規模なVPSでも無理なく実行できます。各メッセージをSMTP(simple mail transfer protocol)サーバーに渡すため、配信率はこのソフトウェアではなく、そのサーバーのレピュテーションによって決まります。

このガイドでは、2026年7月時点の現行リリースであるListmonk v6.2.0をUbuntu 24.04にインストールします。パブリックIPアドレスを持つVPS、管理下にあるドメイン名、PostgreSQL 12以降が必要です。インストールには約1時間かかります。送信レピュテーションの確立には数週間かかります。この点については、ガイドの後半で説明します。

PostgreSQLをインストールしてデータベースを作成する

Ubuntu 24.04は、Listmonkに必要なバージョンを大きく上回るPostgreSQL 16を独自のリポジトリで提供しています。

sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresql

1回のpsqlセッションでロールとデータベースを作成します。-v ON_ERROR_STOP=1を指定すると、最初に失敗したステートメントでpsqlが終了します。そのため、入力ミスによってセットアップの一部だけが完了した状態になることを防げます。

sudo -u postgres psql -v ON_ERROR_STOP=1 <<'SQL'
CREATE USER listmonk WITH PASSWORD 'pick-a-long-random-password';
CREATE DATABASE listmonk OWNER listmonk;
SQL

OWNER listmonkは飾りではありません。スキーマのインストールでは、テーブル、型、インデックス、関数が作成されます。そのため、ロールがデータベースを所有している必要があります。別のロールが所有するデータベースをListmonkに指定すると、GRANT CONNECTを実行した後でも、インストールはpermission deniedで停止します。

先に進む前に、データベースが存在することを確認します。

sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"

listmonkと表示されます。空行の場合は、CREATEステートメントが実行されていません。psqlの出力をもう一度確認してください。

Listmonk バイナリをインストールする

Listmonk は、アーキテクチャごとに静的バイナリをリリースしています。まず使用中のアーキテクチャを確認してください。ARM VPS で amd64 バイナリを実行すると、カーネルに拒否されます。

dpkg --print-architecture
cd /tmp
curl -fsSLO https://github.com/knadh/listmonk/releases/download/v6.2.0/listmonk_6.2.0_linux_amd64.tar.gz
tar -xzf listmonk_6.2.0_linux_amd64.tar.gz
sudo install -m 755 listmonk /usr/bin/listmonk
listmonk --version

ARM VPS では、ファイル名の amd64arm64 に置き換えます。listmonk --version でバージョン文字列が表示されれば、バイナリがマシンに適合していることを最初に確認できます。

config.tomlを生成してアクセスを制限する

--new-configは、現在の作業ディレクトリにconfig.tomlを書き込みます。そのため、cdsh -c内にあり、sudoの前にはありません。

sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'

生成されたファイルは短い内容です。[app]の下で、address = "localhost:9000"はHTTPサーバーをloopbackのみにバインドします。そのため、リバースプロキシを前段に配置するまで、管理パネルはインターネットからアクセスできません。この行は変更しないでください。[db]の下には、host = "localhost"port = 5432user = "listmonk"database = "listmonk"ssl_mode = "disable"があります。これらのデフォルト値は、作成したデータベースにすでに適合しています。変更が必要なのはパスワードの行だけです。

Postgresが同じホスト上でloopbackをリッスンしている間は、ssl_mode = "disable"で問題ありません。この通信はマシンの外に出ないためです。データベースを別のホストに移動する場合は、requireに設定してください。そうしないと、パスワードが平文のままネットワークを通過します。

[db]の下にあるパスワードの行を、ロールに一致するよう編集します。その後、サービスアカウントを作成し、他のすべてのログインアカウントからファイルへのアクセスを外します。

sudo useradd --system --home-dir /var/lib/listmonk --create-home --shell /usr/sbin/nologin listmonk
sudo chown -R root:listmonk /etc/listmonk
sudo chmod 640 /etc/listmonk/config.toml

これで、サービスアカウントだけがファイルを読み取れます。

sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.toml

最初のコマンドはreadableを表示します。2つ目のコマンドはroot:listmonk 640を表示します。権限のない別のアカウントが同じcatを実行すると、Permission deniedになります。これが重要な点です。このファイルにはデータベースのパスワードが平文で保存されており、通常、サーバーには複数のログインアカウントがあります。同じ考え方は、実行するすべてのサービスに当てはまります。そのため、最小権限のサービスユーザーを一度確認し、すべてのサービスに適用してください。

--install でスキーマを作成する

--install はテーブルを作成し、デフォルト設定を投入します。環境変数で最初の管理者ログインを設定してください。これにより、パネルにアクセスできるようになる前にアカウントが作成されます。

sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
  LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
  listmonk --config /etc/listmonk/config.toml --install --yes

--yes は確認プロンプトに応答します。自動化する前に、そのプロンプトを1回確認してください。--install は初回専用のインストーラーであり、既存の Listmonk スキーマを削除します。稼働中のデータベースで2回目に実行すると、購読者が削除されます。2回実行される可能性があるスクリプトでは、テーブルがすでに存在する場合に何もしない --install --idempotent --yes を使用してください。新しいリリースで提供されたスキーマ変更は --upgrade で適用し、--install は使用しません。

結果はブラウザーではなく、データベース側で確認してください。

sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"

1つ目のコマンドは Listmonk のテーブルを一覧表示します。これには subscriberslistscampaignstemplatesbounces などが含まれます。2つ目のコマンドは admin を出力します。2つ目のコマンドの結果が空の場合、環境変数がプロセスに渡っていません。そのため、パネルではブラウザー上で最初のユーザーを作成するよう求められます。

systemd で Listmonk を実行する

/etc/systemd/system/listmonk.service を作成します。

[Unit]
Description=Listmonk newsletter and mailing list manager
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=simple
User=listmonk
Group=listmonk
WorkingDirectory=/var/lib/listmonk
ExecStart=/usr/bin/listmonk --config /etc/listmonk/config.toml
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true

[Install]
WantedBy=multi-user.target

WorkingDirectory が重要なのは、Listmonk がファイルシステム上のメディアアップロードパスなどの相対パスを、WorkingDirectory に対する相対パスとして解決するためです。After=postgresql.service は起動順序を指定するだけで、Postgres が接続を受け付けるまで待機しません。そのため、Listmonk が少し早く起動して接続できない場合に備えて Restart=on-failure が必要です。

sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/

ss では、127.0.0.1:9000 が LISTEN 状態になっていることを確認します。curl が任意の HTTP ステータス行を返せば、サーバーは応答しています。curlConnection refused で失敗する場合、プロセスは起動中に終了しています。journalctl -u listmonk -n 50 --no-pager に原因が記録されます。enable --now が再起動後も有効になる部分です。手動で起動したプロセスは、次回のカーネル更新後には終了しています。

nginx と TLS を前段に配置する

Listmonk は loopback 上でプレーン HTTP を使用するため、nginx が TLS(トランスポート層セキュリティ)を終端し、リクエストを転送します。

server {
    listen 443 ssl;
    server_name lists.example.com;

    client_max_body_size 25m;

    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;
    }
}

購読者のインポートとメディアのアップロードはファイルの POST であるため、client_max_body_size を引き上げる必要があります。nginx はデフォルトで 1 MB を超えるデータを 413 Request Entity Too Large で拒否します。certbot で証明書を発行すると、listen 443 ssl の行と port 80 からのリダイレクトも自動的に書き込まれます。手順は nginx 用 Let's Encrypt 証明書ガイド にあります。port 80 と 443 を開き、9000 は閉じたままにしてください。proxy は loopback 経由で接続するためです。firewall をまだ設定していない場合は、ufw firewall の基本 から始めてください。

次に admin panel を開き、Settings で root URL を https://lists.example.com に設定します。新規インストールでは http://localhost:9000 が設定されており、Listmonk はメールに挿入するすべての unsubscribe link と media URL にこの値を書き込みます。変更前に campaign を送信すると、各 recipient には自分のマシンを指す link が届きます。受信者はその link を使用できず、spam filter からは、自分の domain さえ設定できない送信者のように見えます。

config.toml にない SMTP を接続する

config.toml で SMTP セクションを検索しても、見つかりません。メール設定はデータベースの settings テーブルに保存されており、管理パネルの Settings と SMTP で編集します。そのため、生成されるファイルは短いままです。また、SMTP を変更しても再起動は必要ありません。

SMTP サーバー自体について、現実的な選択肢は2つあります。自分で運用する方法です。この場合、レピュテーションを完全に自分で管理する必要があり、それ自体が独立したプロジェクトになります。Mailcow で独自のメールサーバーを運用するでは、その内容を説明しています。もう1つは、Listmonk をトランザクションリレーに接続し、IP レピュテーションの管理を他者に任せる方法です。

どちらの場合も、STARTTLS ではポート 587 を、暗黙的 TLS ではポート 465 を使用します。送信にポート 25 を使用する計画は立てないでください。ほとんどの VPS プロバイダーでは、新規アカウントのポート 25 がデフォルトでブロックされています。ポート 25 がブロックされると、接続がハングした場合とまったく同じように見えます。パケットが拒否されずに破棄されるため、クライアントはすぐに失敗せず、タイムアウトまで待機します。

信頼して使用する前にテストしてください。リストを作成し、自分のアドレスを購読者として追加して、1人の受信者にキャンペーンを送信します。受信したメッセージを開き、完全なヘッダーを確認します。受信側が追加した Authentication-Results ヘッダーで、SPF と DKIM に合格したかどうかを確認できます。

配信到達性が作業の中心です

Listmonkはメッセージを作成し、リストを管理して、メールを引き渡します。そのメールが受信トレイに届くかどうかは、受信側プロバイダーが送信IPアドレスと送信ドメインを使って判断します。新しいVPSのIPには履歴がまったくありません。大手メールボックスプロバイダーは、履歴のないIPをやや不審なものとして扱います。

次の4つは必須です。

  • ドメインに代わって送信を許可するホストを指定するSPF (sender policy framework) TXTレコード。
  • TXTレコードとして公開するDKIM (domainkeys identified mail) 鍵。署名はListmonkではなくメールサーバーが行います。
  • DMARC (domain based message authentication, reporting and conformance) レコード。最初の2つの認証に失敗した場合の処理を受信側に指示します。
  • Listmonkが読み取るバウンスメールボックス。メールを拒否したアドレスをリストから外し、無期限に再試行しないようにします。

最初は送信速度も抑えてください。メールを送信したことのないドメインが、突然1時間で10,000件のメッセージを配信すると、侵害されたアカウントとまったく同じ特徴になります。そのため、侵害されたアカウントと同様にフィルタリングされます。まず反応の高い購読者に送信し、数日かけて量を増やしてください。

すべてのテンプレートには、機能する購読解除リンクも必要です。Listmonkのテンプレートでは、それが{{ UnsubscribeURL }}です。キャンペーン本文は{{ template "content" . }}の位置に入り、各テンプレートに正確に1回だけ記述する必要があります。購読解除リンクのないキャンペーンは、購読解除ではなくスパム報告を招きます。スパム報告が増えると、数週間かけて築いた送信レピュテーションを最も早く失います。

バックアップと、リストアに実際に必要なもの

サーバーの外部に保存する必要があるのは、データベースダンプとconfig.tomlです。キャンペーンに画像をアップロードする場合は、メディアディレクトリも追加します。

sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dump

このダンプには、購読者、キャンペーン、テンプレート、SMTP認証情報を含むすべての設定が保存されます。そのため、暗号化してこのサーバー以外に保管してください。スケジュール実行は解決済みの問題です。リモートストレージへの暗号化されたresticバックアップを参照してください。config.tomlは数行のファイルですが、データベースパスワードが含まれています。そのため、同じ方法で扱ってください。

アップグレードは次の順序で実行します。サービスを停止し、ダンプを取得し、/usr/binのバイナリを置き換え、listmonk --config /etc/listmonk/config.toml --upgradeを実行してから、サービスを起動します。スキーマの移行は前方向にのみ実行されるため、元に戻す唯一の方法はこのダンプです。

Listmonk の起動に失敗するのはなぜですか?

まず journalctl -u listmonk -n 50 --no-pager で journal を確認します。ほぼすべての起動失敗は、[db] ブロック内の1行に示されます。

pq: password authentication failed for user "listmonk" は、[db] に設定したパスワードが Postgres ロールと一致しないことを示します。pq プレフィックスは、Postgres driver がサーバーによる拒否を報告していることを示します。つまり、設定は正しく読み込まれており、認証情報が誤っています。sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" でロールをリセットし、同じ文字列をファイルに設定します。

pq: database "listmonk" does not exist は、[db]database の値が実在するデータベースを指定していないことを示します。sudo -u postgres psql -l には、実際にサーバー上に存在するデータベースが一覧表示されます。誤って使用したスペルも確認できます。

--install 中の permission denied は、ロールは接続できるものの、データベースの所有者ではないため、テーブルを作成できないことを示します。sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" で修正し、再度インストールを実行します。

サービスが起動せず、journal に設定ファイルが示される場合。 listmonk として実行されるプロセスは、mode 600 の root:root が設定された config.toml を開けません。stat -c '%U:%G %a' /etc/listmonk/config.toml の出力は root:listmonk 640 になる必要があります。また、その親ディレクトリは root:listmonk 750 である必要があります。

パネルは動作するものの、メールが届かない場合。 これは起動の問題ではありません。まず Settings と SMTP を確認します。次に、admin panel にあるキャンペーン固有の log を確認します。この log には、各送信試行で mail server が返したエラーが記録されています。

FAQ

Listmonkを使用するには、自分のメールサーバーが必要ですか?

いいえ。Listmonkはメールサーバーではありません。メールを受け付けて配信するサーバーのSMTP認証情報が必要です。トランザクションリレーでも、自分で運用するメールサーバーでも構いません。管理画面のSettingsとSMTPで認証情報を設定してください。メール設定はデータベースに保存されるため、config.tomlには設定しません。新規アカウントでは、多くのVPSプロバイダーが送信ポート25をブロックするため、STARTTLSを使用するポート587、または暗黙的TLSを使用するポート465を設定してください。

キャンペーン内のリンクがlocalhost:9000を指すのはなぜですか?

ルートURLの設定が、インストール時のデフォルト値http://localhost:9000のままです。Listmonkは、キャンペーンの送信時にこの値を購読解除リンクとメディアURLへ書き込みます。管理画面でSettingsを開き、ルートURLを実際のHTTPSアドレスに設定して保存してください。配信済みのメッセージは修正できません。そのため、実際のリストに送信する前にテストキャンペーンを自分宛てに送り、含まれている購読解除リンクをクリックしてください。

--installを再度実行すると、購読者は消去されますか?

はい。--installは初回インストーラーで、既存のListmonkスキーマを削除します。--yesは、警告を表示する確認プロンプトを削除します。2回実行される可能性のあるスクリプトでは、--install --idempotent --yesを使用してください。テーブルがすでに存在する場合、何も実行しません。新しいリリースでスキーマの変更を適用するには、サービスを停止し、pg_dumpを取得してから、--upgradeを実行してください。

Listmonkが、ユーザーlistmonkのパスワード認証に失敗したと表示するのはなぜですか?

/etc/listmonk/config.toml[db]ブロックにあるパスワードが、同名のPostgresロールのパスワードと一致していません。journalの行はpq: password authentication failed for user "listmonk"です。pqは、サーバーによる拒否をそのまま伝えているPostgresドライバーです。つまり、設定ファイルは見つかり、読み込まれています。sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';"でロールのパスワードをリセットし、同じ文字列を設定ファイルに書き込んでから、sudo systemctl restart listmonkを実行してください。