Installer Listmonk sur Ubuntu 24.04 avec un VPS
Installez Listmonk v6.2.0 sur Ubuntu 24.04 avec PostgreSQL, systemd, TLS et SMTP. Découvrez pourquoi la délivrabilité dépend surtout de la réputation IP.
Ce dont une newsletter auto-hébergée avec Listmonk a besoin
Listmonk est un gestionnaire auto-hébergé de newsletters et de listes de diffusion : un binaire Go, une base de données PostgreSQL, un fichier de configuration et une unité systemd. Un petit VPS suffit pour l’exécuter, car Listmonk stocke les abonnés et met les campagnes en file d’attente, mais n’envoie jamais lui-même les e-mails. Il remet chaque message à un serveur SMTP (simple mail transfer protocol). Votre taux de délivrabilité dépend donc de la réputation de ce serveur, et non de ce logiciel.
Ce guide installe Listmonk v6.2.0, la version actuelle en juillet 2026, sur Ubuntu 24.04. Vous avez besoin d’un VPS doté d’une adresse IP publique, d’un nom de domaine que vous contrôlez et de PostgreSQL 12 ou version ultérieure. L’installation prend environ une heure. La réputation d’envoi se construit sur plusieurs semaines. Cette partie est traitée vers la fin du guide.
Installer PostgreSQL et créer la base de données
Ubuntu 24.04 fournit PostgreSQL 16 dans son propre dépôt, bien au-delà de la version requise par Listmonk.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlCréez le rôle et la base de données dans une seule session psql. -v ON_ERROR_STOP=1 oblige psql à quitter dès la première instruction en échec. Ainsi, une faute de frappe ne vous laisse pas avec une configuration partielle qui semble terminée.
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;
SQLOWNER listmonk n’est pas facultatif. L’installation du schéma crée des tables, des types, des index et des fonctions. Le rôle doit donc être propriétaire de la base de données. Si vous configurez Listmonk avec une base appartenant à un autre rôle, l’installation s’arrête avec permission denied, même après l’exécution de GRANT CONNECT.
Vérifiez que la base de données existe avant de continuer.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Cette commande affiche listmonk. Une ligne vide signifie que l’instruction CREATE ne s’est jamais exécutée. Relisez donc la sortie de psql.
Installer le binaire Listmonk
Listmonk publie un binaire statique par architecture. Vérifiez d’abord la vôtre, car un binaire amd64 sur un VPS ARM est un fichier que le noyau refuse d’exécuter.
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 --versionSur un VPS ARM, remplacez amd64 par arm64 dans le nom du fichier. Le fait que listmonk --version affiche une chaîne de version confirme d’abord que le binaire correspond à la machine.
Générer config.toml et en restreindre l’accès
--new-config écrit config.toml dans le répertoire de travail courant. C’est pourquoi le cd se trouve dans le sh -c, et non avant sudo.
sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'Le fichier généré est court. Sous [app], address = "localhost:9000" lie le serveur HTTP à loopback uniquement. Le panneau d’administration n’est donc pas accessible depuis Internet tant que vous ne placez pas de reverse proxy devant lui. Ne modifiez pas cette ligne. Sous [db], vous trouvez host = "localhost", port = 5432, user = "listmonk", database = "listmonk" et ssl_mode = "disable". Ces valeurs par défaut correspondent déjà à la base de données que vous avez créée. Vous devez donc uniquement modifier la ligne du mot de passe.
ssl_mode = "disable" convient lorsque Postgres écoute sur loopback, sur la même machine, car ce trafic ne quitte jamais la machine. Si vous déplacez la base de données vers un autre hôte, définissez cette valeur sur require. Sinon, le mot de passe circule sur le réseau en clair.
Modifiez la ligne du mot de passe sous [db] pour qu’elle corresponde au rôle, puis créez le compte de service et retirez le fichier à tous les autres comptes de connexion.
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.tomlLe compte de service peut maintenant lire le fichier, et personne d’autre ne le peut.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlLa première commande affiche readable. La deuxième affiche root:listmonk 640. Tout autre compte non privilégié qui tente le même cat obtient Permission denied. C’est le but recherché : ce fichier contient votre mot de passe de base de données en clair, et un serveur possède généralement plusieurs comptes de connexion. Le même principe s’applique à chaque service que vous exécutez. Lisez donc les comptes de service avec privilèges minimaux et appliquez ce principe partout.
Créer le schéma avec --install
--install crée les tables et initialise les paramètres par défaut. Définissez le premier identifiant de connexion administrateur avec des variables d’environnement afin que le compte existe avant que le panneau soit accessible.
sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
listmonk --config /etc/listmonk/config.toml --install --yes--yes répond à l’invite de confirmation. Lisez cette invite une fois avant de l’automatiser, car --install est l’opération d’installation initiale et supprime un schéma Listmonk existant. Une deuxième exécution sur une base de données en production détruit vos abonnés. Dans tout script susceptible d’être exécuté deux fois, utilisez --install --idempotent --yes, qui ne fait rien lorsque les tables existent déjà. Les modifications de schéma incluses dans une nouvelle version s’appliquent avec --upgrade, jamais avec --install.
Vérifiez le résultat depuis la base de données plutôt que depuis le navigateur.
sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"La première commande liste les tables de Listmonk, notamment subscribers, lists, campaigns, templates et bounces. La seconde affiche admin. Un résultat vide pour la seconde commande signifie que les variables d’environnement n’ont jamais été transmises au processus. Le panneau vous demandera alors de créer le premier utilisateur dans le navigateur.
Exécuter Listmonk avec systemd
Écrivez /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.targetWorkingDirectory est important, car Listmonk résout les chemins relatifs, notamment le chemin du système de fichiers utilisé pour les téléversements multimédias, par rapport à ce répertoire. After=postgresql.service ordonne uniquement le démarrage. Il n’attend pas que Postgres accepte les connexions. Restart=on-failure couvre donc le cas où Listmonk démarre un peu trop tôt et ne parvient pas à se connecter.
sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/ss doit afficher 127.0.0.1:9000 dans l’état LISTEN. Si curl renvoie une ligne d’état HTTP, cela signifie que le serveur répond. Si curl échoue avec Connection refused, le processus s’est arrêté pendant le démarrage. journalctl -u listmonk -n 50 --no-pager indiquera la cause. Notez que enable --now est la partie qui survit à un redémarrage. Un processus démarré manuellement disparaît après la prochaine mise à niveau du kernel.
Placer nginx et TLS en frontal
Listmonk utilise HTTP en clair sur l’interface loopback. nginx termine donc TLS (transport layer security) et transmet la requête.
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;
}
}Il faut augmenter client_max_body_size, car les imports d’abonnés et les téléversements de médias sont des envois de fichiers. Par défaut, nginx rejette les fichiers de plus de 1 MB avec 413 Request Entity Too Large. Émettez le certificat avec certbot. certbot écrit également les lignes listen 443 ssl et la redirection depuis le port 80. Les étapes sont décrites dans le guide des certificats Let's Encrypt pour nginx. Ouvrez les ports 80 et 443 et laissez le port 9000 fermé, car le proxy y accède via l’interface loopback. Si le pare-feu n’est pas encore configuré, commencez par les bases du pare-feu ufw.
Ouvrez ensuite le panneau d’administration et définissez l’URL racine dans Settings sur https://lists.example.com. Une nouvelle installation contient http://localhost:9000. Listmonk utilise cette valeur dans chaque lien de désabonnement et chaque URL de média qu’il insère dans un e-mail. Si vous envoyez une campagne avant de la modifier, chaque destinataire reçoit des liens qui pointent vers sa propre machine. Ces liens échouent pour le destinataire. Pour un filtre antispam, ils indiquent que l’expéditeur ne sait pas configurer son propre domaine.
Connecter SMTP, qui n’est pas dans config.toml
Recherchez une section SMTP dans config.toml : vous n’en trouverez aucune. Les paramètres de messagerie sont stockés dans la base de données, dans la table settings, et vous les modifiez dans le panneau d’administration, sous Settings et SMTP. C’est pourquoi le fichier généré reste très court. C’est aussi pourquoi une modification de la configuration SMTP ne nécessite aucun redémarrage.
Il existe deux options réalistes pour le serveur SMTP lui-même. Vous pouvez gérer votre propre serveur. Vous gardez ainsi entièrement le contrôle de sa réputation, mais il s’agit d’un projet à part entière : gérer votre propre serveur de messagerie avec Mailcow explique ce que cela implique. Vous pouvez aussi configurer Listmonk pour utiliser un relais transactionnel et laisser un autre prestataire gérer la réputation de l’adresse IP.
Dans les deux cas, utilisez le port 587 avec STARTTLS, ou le port 465 avec TLS implicite. Ne prévoyez pas d’utiliser le port sortant 25. La plupart des fournisseurs de VPS le bloquent par défaut sur les nouveaux comptes. Un port 25 bloqué ressemble exactement à une connexion bloquée, car les paquets sont abandonnés au lieu d’être refusés. Le client attend donc l’expiration du délai au lieu d’échouer immédiatement.
Testez la configuration avant de lui faire confiance. Créez une liste, ajoutez votre propre adresse comme abonné, puis envoyez une campagne à un seul destinataire. Ouvrez le message reçu et consultez les en-têtes complets. L’en-tête Authentication-Results ajouté par le serveur de réception vous indique si SPF et DKIM ont réussi.
La délivrabilité est l’objectif principal
Listmonk construit le message, gère la liste et remet le message au serveur de messagerie. Le fournisseur destinataire prend toutes les décisions concernant la réception de ce message dans une boîte de réception, en fonction de l’adresse IP d’envoi et du domaine d’envoi. Une nouvelle adresse IP de VPS n’a aucun historique. Tous les grands fournisseurs de boîtes aux lettres considèrent l’absence d’historique comme un indice légèrement suspect.
Quatre éléments sont indispensables :
- Un enregistrement TXT SPF (sender policy framework) qui indique l’hôte autorisé à envoyer des messages pour votre domaine.
- Une clé DKIM (domainkeys identified mail) publiée sous forme d’enregistrement TXT. La signature doit être effectuée par le serveur de messagerie, et non par Listmonk.
- Un enregistrement DMARC (domain based message authentication, reporting and conformance), qui indique aux serveurs destinataires la conduite à tenir lorsque les 2 premiers contrôles échouent.
- Une boîte aux lettres de rebond que Listmonk consulte. Les adresses qui refusent les messages sont ainsi retirées de la liste au lieu de faire l’objet de nouvelles tentatives indéfinies.
Envoyez ensuite les messages progressivement au début. Un domaine qui n’a jamais envoyé de messages et qui en remet soudainement dix mille en une heure ressemble exactement à un compte compromis. Il est donc filtré comme tel. Commencez par vos abonnés les plus engagés et augmentez le volume sur plusieurs jours.
Chaque modèle doit également contenir un lien de désabonnement fonctionnel. Dans un modèle Listmonk, ce lien est {{ UnsubscribeURL }}. Le corps de la campagne est inséré à l’emplacement de {{ template "content" . }}, qui doit apparaître exactement une fois par modèle. Une campagne sans lien de désabonnement provoque des signalements comme spam au lieu de désabonnements. Ces signalements sont le moyen le plus rapide de perdre une réputation d’envoi que vous avez mis des semaines à établir.
Sauvegardes et éléments nécessaires à une restauration
Deux éléments doivent sortir du serveur : le dump de la base de données et config.toml. Ajoutez le répertoire des médias si vous téléversez des images dans les campagnes.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpCe dump contient les abonnés, les campagnes, les templates et tous les paramètres, y compris les identifiants SMTP. Chiffrez-le et conservez-le sur un autre serveur. La planification est simple : consultez sauvegardes restic chiffrées vers un stockage distant. config.toml ne contient que quelques lignes, mais il contient le mot de passe de la base de données. Traitez-le donc de la même manière.
Les mises à niveau suivent un ordre précis. Arrêtez le service, effectuez un dump, remplacez le binaire dans /usr/bin, exécutez listmonk --config /etc/listmonk/config.toml --upgrade, puis démarrez le service. Les migrations de schéma s’exécutent uniquement vers l’avant. Ce dump est donc votre seul moyen de revenir à l’état précédent.
Pourquoi Listmonk refuse-t-il de démarrer ?
Lisez d'abord le journal avec journalctl -u listmonk -n 50 --no-pager. Presque chaque échec au démarrage correspond à une ligne du bloc [db].
pq: password authentication failed for user "listmonk" signifie que le mot de passe dans [db] ne correspond pas au rôle Postgres. Le préfixe pq indique que le pilote Postgres signale le refus du serveur. La configuration a donc été lue correctement, mais les identifiants sont incorrects. Réinitialisez le rôle avec sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" et utilisez la même chaîne dans le fichier.
pq: database "listmonk" does not exist signifie que la valeur database dans [db] ne désigne pas une base de données existante. sudo -u postgres psql -l affiche les bases réellement présentes sur le serveur, y compris l'orthographe saisie par erreur.
permission denied pendant --install signifie que le rôle peut se connecter, mais qu'il n'est pas propriétaire de la base de données. Il ne peut donc pas y créer de tables. Corrigez cela avec sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;", puis relancez l'installation.
Le service ne démarre jamais et le journal indique le fichier de configuration. Un processus exécuté sous listmonk ne peut pas ouvrir un fichier config.toml dont les permissions sont définies sur root:root avec le mode 600. stat -c '%U:%G %a' /etc/listmonk/config.toml doit afficher root:listmonk 640, et le répertoire parent doit être root:listmonk 750.
Le panel fonctionne, mais aucun e-mail n'arrive. Ce n'est pas un problème de démarrage. Vérifiez d'abord Settings et SMTP, puis le journal de la campagne dans le panel d'administration. Il indique l'erreur renvoyée par le serveur de messagerie pour chaque tentative.
FAQ
Ai-je besoin de mon propre serveur de messagerie pour utiliser Listmonk ?
Non. Listmonk n’est pas un serveur de messagerie. Il a besoin des identifiants SMTP d’un serveur qui accepte vos messages et les distribue. Il peut s’agir d’un relais transactionnel ou d’un serveur de messagerie que vous administrez vous-même. Renseignez ces identifiants dans Settings et SMTP du panneau d’administration, et non dans config.toml, car les paramètres de messagerie sont stockés dans la base de données. Utilisez le port 587 avec STARTTLS ou le port 465 avec TLS implicite, car la plupart des fournisseurs de VPS bloquent le trafic sortant sur le port 25 pour les nouveaux comptes.
Pourquoi les liens de ma campagne pointent-ils vers localhost:9000 ?
Le paramètre de l’URL racine utilise encore sa valeur d’installation par défaut, http://localhost:9000. Listmonk écrit cette valeur dans les liens de désabonnement et les URL des médias au moment de l’envoi d’une campagne. Ouvrez Settings dans le panneau d’administration, définissez l’URL racine sur votre véritable adresse HTTPS, puis enregistrez. Les messages déjà distribués ne peuvent pas être corrigés. Envoyez donc une campagne de test à votre adresse et cliquez sur le lien de désabonnement avant d’envoyer un message à une liste réelle.
L’exécution de --install une nouvelle fois supprimera-t-elle mes abonnés ?
Oui. --install est l’installateur initial et supprime le schéma Listmonk existant. --yes supprime l’invite qui vous aurait averti. Dans tout script susceptible d’être exécuté deux fois, utilisez --install --idempotent --yes. Cette option ne fait rien lorsque les tables existent déjà. Pour appliquer les modifications de schéma d’une nouvelle release, arrêtez le service, créez une pg_dump, puis exécutez --upgrade.
Pourquoi Listmonk indique-t-il que l’authentification par mot de passe a échoué pour l’utilisateur listmonk ?
Le mot de passe du bloc [db] dans /etc/listmonk/config.toml ne correspond pas au rôle Postgres du même nom. La ligne du journal est pq: password authentication failed for user "listmonk". pq correspond au driver Postgres qui transmet le refus du serveur. Cela signifie que le fichier de configuration a été trouvé et lu. Réinitialisez le mot de passe du rôle avec sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", écrivez la même chaîne dans le fichier de configuration, puis exécutez sudo systemctl restart listmonk.