SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-07

ListmonkをUbuntu 24.04のVPSで自ホストする方法

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 の unit が 1 つあれば動作します。Listmonk は購読者を保存し、キャンペーンをキューに入れますが、メール自体を配信することはありません。そのため、小規模な VPS でも無理なく運用できます。各メッセージは SMTP (simple mail transfer protocol) サーバーに渡されます。したがって、メールの到達率を決めるのは Listmonk ではなく、SMTP サーバーのレピュテーションです。

このガイドでは、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 のみにバインドするため、reverse proxy を前段に置くまで管理パネルはインターネットから到達できません。この行は変更しないでください。[db] の下には、host = "localhost"port = 5432user = "listmonk"database = "listmonk"ssl_mode = "disable" が設定されます。これらのデフォルト値は、作成したデータベースにすでに適合しているため、変更が必要なのはパスワードの行だけです。

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

[db] の下にあるパスワードの行を、role に一致するよう編集します。次に service account を作成し、他のすべての login からファイルを読み取れないようにします。

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

これで service account はファイルを読み取れますが、それ以外のユーザーは読み取れません。

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 を出力します。他の unprivileged account が同じ cat を実行すると Permission denied になります。これが重要です。このファイルにはデータベースのパスワードが平文で保存されており、通常、サーバーには複数の login が存在するためです。同じ考え方は、実行するすべてのサービスに適用されます。最小権限の service user を一度読んで、すべてのサービスに適用してください。

--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 は確認プロンプトに応答します。自動化する前に、そのプロンプトを一度確認してください。--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

Listmonk はファイルシステム上のメディアアップロードパスなど、相対パスを WorkingDirectory を基準に解決するため、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 で証明書を発行してください。certbot は listen 443 ssl の行と port 80 からのリダイレクトも自動的に追加します。手順は nginx 用 Let's Encrypt 証明書ガイド にあります。port 80 と 443 を開放し、9000 は閉じたままにします。プロキシは loopback 経由で接続するためです。ファイアウォールをまだ設定していない場合は、ufw ファイアウォールの基本 から始めてください。

次に管理画面を開き、Settings の root URL に https://lists.example.com を設定します。新規インストールでは http://localhost:9000 になっており、Listmonk はメールに記載するすべての配信停止リンクとメディア URL にこの値を書き込みます。変更前にキャンペーンを送信すると、各受信者には自分のマシンを指すリンクが届きます。受信者はそのリンクを開けず、スパムフィルターからは、自分のドメインを設定できていない送信者と見なされます。

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

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

SMTP サーバー自体については、現実的な選択肢が 2 つあります。自分で運用する方法です。この場合、レピュテーションをすべて自分で管理する必要があり、それ自体が 1 つのプロジェクトになります。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 ではなく、メールサーバーが行います。
  • 最初の2つの認証に失敗した場合の処理を受信側に指示する DMARC (domain based message authentication, reporting and conformance) レコード。
  • Listmonk が読み取るバウンス用メールボックス。メールを拒否したアドレスをリストから削除し、無期限に再試行しないようにします。

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

すべてのテンプレートには、機能する配信停止リンクも必要です。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 ドライバーがサーバーによる拒否を報告しているものです。そのため、設定は正しく読み込まれており、認証情報が誤っています。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 として実行されるプロセスは、root:root の所有者で mode 600 に設定された config.toml を開けません。stat -c '%U:%G %a' /etc/listmonk/config.toml の出力は root:listmonk 640 になり、1 つ上のディレクトリは root:listmonk 750 になっている必要があります。

パネルは動作するものの、メールが届かない場合。 これは起動の問題ではありません。まず Settings と SMTP を確認し、次に管理パネルにあるキャンペーン固有のログを確認します。このログには、試行ごとにメールサーバーが返したエラーが記録されます。

FAQ

Listmonk の利用に独自のメールサーバーは必要ですか?

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

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

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

--install を再実行すると購読者は削除されますか?

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

Listmonk が user 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 を実行してください。