Listmonk zelf hosten op Ubuntu 24.04: handleiding
Installeer Listmonk v6.2.0 op een Ubuntu 24.04 VPS met PostgreSQL en systemd. Leer hoe u SMTP koppelt en ontdek waarom uw verzendreputatie cruciaal is voor aflevering.
Wat een self-hosted nieuwsbrief op Listmonk vereist
Listmonk is een self-hosted beheerder voor nieuwsbrieven en mailinglijsten: één Go-binary, één PostgreSQL-database, één configuratiebestand, één systemd-unit. Een kleine VPS voert dit moeiteloos uit, omdat Listmonk abonnees opslaat en campagnes in de wachtrij plaatst, maar de e-mail zelf nooit verstuurt. Het draagt elk bericht over aan een SMTP (simple mail transfer protocol)-server, waardoor uw afleverpercentage wordt bepaald door de reputatie van die server en niet door deze software.
Deze handleiding installeert Listmonk v6.2.0, de huidige release per juli 2026, op Ubuntu 24.04. U heeft een VPS nodig met een publiek IP-adres, een domeinnaam die u beheert en PostgreSQL 12 of nieuwer. De installatie duurt ongeveer een uur. Het opbouwen van een verzendreputatie kost weken; dat onderdeel wordt tegen het einde behandeld.
PostgreSQL installeren en de database aanmaken
Ubuntu 24.04 levert PostgreSQL 16 in de eigen repository, wat ruimschoots voldoet aan de vereisten van Listmonk.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlMaak de rol en de database aan in één psql-sessie. -v ON_ERROR_STOP=1 zorgt ervoor dat psql afsluit bij de eerste mislukte instructie, zodat een typefout niet resulteert in een gedeeltelijke configuratie die er voltooid uitziet.
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 is geen decoratie. De schema-installatie maakt tabellen, types, indexen en functies aan, dus de rol moet eigenaar zijn van de database. Als u Listmonk koppelt aan een database die eigendom is van een andere rol, stopt de installatie met permission denied, zelfs nadat u GRANT CONNECT heeft uitgevoerd.
Controleer of de database bestaat voordat u verdergaat.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Dit voert listmonk uit. Een lege regel betekent dat de CREATE-instructie nooit is uitgevoerd; lees daarom de psql-uitvoer opnieuw.
De Listmonk-binary installeren
Listmonk publiceert een statische binary per architectuur. Controleer eerst uw architectuur, aangezien een amd64-binary op een ARM-VPS een bestand is dat de kernel weigert uit te voeren.
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 --versionVervang op een ARM-VPS amd64 door arm64 in de bestandsnaam. listmonk --version het afdrukken van een versieregel is uw eerste bewijs dat de binary overeenkomt met de machine.
Genereer config.toml en beveilig het bestand
--new-config schrijft config.toml naar de huidige werkmap. Daarom staat cd in de sh -c, en niet voor sudo.
sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'Het gegenereerde bestand is kort. Onder [app] bindt address = "localhost:9000" de HTTP-server uitsluitend aan loopback, zodat het beheerderspaneel niet bereikbaar is vanaf het internet totdat u er een reverse proxy voor plaatst. Wijzig deze regel niet. Onder [db] vindt u host = "localhost", port = 5432, user = "listmonk", database = "listmonk" en ssl_mode = "disable". Deze standaardwaarden komen al overeen met de database die u heeft aangemaakt, dus de enige regel die u moet aanpassen is het wachtwoord.
ssl_mode = "disable" is correct zolang Postgres op loopback luistert op dezelfde machine, omdat dat verkeer de machine nooit verlaat. Verplaats de database naar een andere host en stel deze in op require, anders wordt het wachtwoord in leesbare tekst over het netwerk verstuurd.
Bewerk de wachtwoordregel onder [db] zodat deze overeenkomt met de rol, maak vervolgens het service-account aan en ontneem alle andere gebruikers de toegang tot het bestand.
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.tomlNu kan het service-account het bestand lezen en niemand anders.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlHet eerste commando toont readable. Het tweede toont root:listmonk 640. Elk ander account zonder privileges dat hetzelfde cat probeert, krijgt Permission denied, en dat is precies de bedoeling: dit bestand bevat uw databasewachtwoord in leesbare tekst, en een server heeft doorgaans meer dan één login. Dezelfde redenering is van toepassing op elke service die u draait, dus lees servicegebruikers met minimale rechten eenmaal door en pas dit overal toe.
Het schema aanmaken met --install
--install bouwt de tabellen en vult deze met de standaardinstellingen. Stel de eerste admin-login in via omgevingsvariabelen, zodat het account bestaat voordat het paneel bereikbaar is.
sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
listmonk --config /etc/listmonk/config.toml --install --yes--yes beantwoordt de bevestigingsvraag. Lees die vraag eerst door voordat u deze automatiseert, omdat --install de installer voor de eerste keer is en een bestaand Listmonk-schema verwijdert. Het een tweede keer uitvoeren op een actieve database vernietigt uw abonnees. Gebruik in elk script dat mogelijk twee keer wordt uitgevoerd --install --idempotent --yes; dit doet niets als de tabellen al aanwezig zijn. Schemawijzigingen die in een nieuwe release worden uitgebracht, worden toegepast met --upgrade, nooit met --install.
Controleer het resultaat aan de kant van de database in plaats van in de browser.
sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"De eerste somt de Listmonk-tabellen op, waaronder subscribers, lists, campaigns, templates en bounces. De tweede print admin. Een leeg resultaat van de tweede betekent dat de omgevingsvariabelen het proces nooit hebben bereikt, waardoor het paneel u in de browser zal vragen om de eerste gebruiker aan te maken.
Listmonk uitvoeren onder systemd
Schrijf /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 is van belang omdat Listmonk relatieve paden, waaronder het bestandssysteem-pad voor media-uploads, hieraan relateert. After=postgresql.service regelt enkel de volgorde van het opstarten; het wacht niet tot Postgres verbindingen accepteert. Daarom dekt Restart=on-failure het scenario waarin Listmonk net iets te vroeg start en geen verbinding kan maken.
sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/ss hoort 127.0.0.1:9000 in de status LISTEN te tonen. Als curl een HTTP-statusregel teruggeeft, betekent dit dat de server antwoordt. Als curl faalt met Connection refused, betekent dit dat het proces tijdens het opstarten is beëindigd; journalctl -u listmonk -n 50 --no-pager geeft aan waarom. Let op: enable --now is het onderdeel dat een reboot overleeft; een handmatig gestart proces is na de volgende kernel-upgrade verdwenen.
Plaats nginx en TLS voor de applicatie
Listmonk communiceert via standaard HTTP op localhost, dus nginx handelt de TLS-terminatie (transport layer security) af en stuurt het verkeer door.
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;
}
}client_max_body_size moet worden verhoogd omdat het importeren van abonnees en het uploaden van media via file posts verloopt, en nginx weigert standaard alles boven 1 MB met 413 Request Entity Too Large. Vraag het certificaat aan met certbot; dit hulpprogramma schrijft ook de listen 443 ssl-regels en de redirect vanaf poort 80 voor u: de stappen staan in de Let's Encrypt-certificaathandleiding voor nginx. Open poort 80 en 443 en laat poort 9000 gesloten, aangezien de proxy deze via localhost bereikt. Als de firewall nog niet is geconfigureerd, begin dan met de basisprincipes van de ufw-firewall.
Open vervolgens het beheerpaneel en stel de root URL onder Settings in op https://lists.example.com. Een nieuwe installatie bevat http://localhost:9000, en Listmonk verwerkt deze waarde in elke afmeldlink en media-URL die in een e-mail wordt geplaatst. Verstuurt u een campagne voordat u dit wijzigt, dan krijgen ontvangers links die naar hun eigen machine verwijzen. Deze werken niet voor de lezer, en voor een spamfilter ziet het eruit als een afzender die het eigen domein niet correct kan configureren.
SMTP verbinden, wat niet in config.toml staat
Zoek in config.toml naar een SMTP-sectie en u zult deze niet vinden. De e-mailinstellingen bevinden zich in de database, in de tabel settings, en u bewerkt deze in het beheerderspaneel onder Settings en SMTP. Daarom blijft het gegenereerde bestand zo kort, en daarom is voor een wijziging in SMTP geen herstart vereist.
Er zijn twee reële opties voor de SMTP-server zelf. Draai uw eigen server, wat de reputatie volledig in uw eigen handen legt en op zichzelf een aanzienlijk project is: uw eigen mailserver draaien met Mailcow behandelt wat daarbij komt kijken. Of wijs Listmonk naar een transactionele relay en laat iemand anders de IP-reputatie beheren.
Gebruik in beide gevallen poort 587 met STARTTLS, of poort 465 met impliciete TLS. Reken niet op uitgaande poort 25. De meeste VPS-providers blokkeren deze standaard op nieuwe accounts, en een geblokkeerde poort 25 ziet er precies zo uit als een hangende verbinding, omdat de pakketten worden genegeerd in plaats van geweigerd. Hierdoor wacht de client op een timeout in plaats van direct te falen.
Test het voordat u het vertrouwt. Maak een lijst aan, voeg uw eigen adres toe als abonnee en verstuur een campagne naar één ontvanger. Open het ontvangen bericht en lees de volledige headers. De header Authentication-Results die door de ontvangende partij wordt toegevoegd, vertelt u of SPF en DKIM zijn geslaagd.
Afleverbaarheid is de volledige taak
Listmonk stelt het bericht samen, beheert de lijst en draagt de e-mail over. Elke beslissing over de vraag of die e-mail een inbox bereikt, wordt genomen door de ontvangende provider op basis van het verzendende IP-adres en het verzendende domein. Een nieuw VPS IP-adres heeft geen enkele historie, en het ontbreken van historie wordt door elke grote mailboxprovider als licht verdacht beschouwd.
Vier zaken zijn niet optioneel:
- Een SPF (sender policy framework) TXT-record dat de host benoemt die namens uw domein mag verzenden.
- Een DKIM (domainkeys identified mail) key die als TXT-record is gepubliceerd, waarbij het ondertekenen door de mailserver wordt gedaan in plaats van door Listmonk.
- Een DMARC (domain based message authentication, reporting and conformance) record, dat ontvangers instrueert wat zij moeten doen wanneer de eerste twee falen.
- Een bounce-mailbox die Listmonk uitleest, zodat adressen die e-mail weigeren van de lijst worden verwijderd in plaats van dat er oneindig opnieuw wordt geprobeerd.
Verzend in het begin langzaam. Een domein dat nog nooit e-mail heeft verzonden en plotseling tienduizend berichten in een uur aflevert, vertoont exact het patroon van een gecompromitteerd account en wordt daarom als zodanig gefilterd. Begin met uw meest betrokken abonnees en verhoog het volume over een periode van dagen.
Elke template heeft ook een werkende afmeldlink nodig. In een Listmonk-template is dat {{ UnsubscribeURL }}, en de inhoud van de campagne komt op de plek waar {{ template "content" . }} staat, wat exact één keer per template moet voorkomen. Een campagne zonder afmeldlink levert spamklachten op in plaats van afmeldingen, en klachten zijn de snelste manier om een verzendreputatie te verliezen waar u weken aan heeft gewerkt.
Backups, en wat een restore daadwerkelijk vereist
Twee zaken moeten de server verlaten: de database-dump en config.toml. Voeg de media-directory toe als u afbeeldingen uploadt naar campagnes.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpDie dump bevat abonnees, campagnes, templates en elke instelling, inclusief de SMTP-inloggegevens. Versleutel deze daarom en bewaar de dump buiten deze server. Het inplannen hiervan is een opgelost probleem: zie versleutelde restic-backups naar externe opslag. config.toml bestaat uit enkele regels, maar bevat het database-wachtwoord; behandel dit bestand dus op dezelfde wijze.
Upgrades volgen één vaste volgorde. Stop de service, maak een dump, vervang het binaire bestand in /usr/bin, voer listmonk --config /etc/listmonk/config.toml --upgrade uit en start vervolgens de service. Schema-migraties kunnen alleen vooruit worden uitgevoerd, dus die dump is uw enige weg terug.
Waarom start Listmonk niet op?
Lees eerst het logboek met journalctl -u listmonk -n 50 --no-pager. Vrijwel elke opstartfout staat op één regel in het [db]-blok.
pq: password authentication failed for user "listmonk" betekent dat het wachtwoord in [db] niet overeenkomt met de Postgres-rol. Het pq-voorvoegsel is de Postgres-driver die de afwijzing van de server rapporteert; de configuratie is dus correct ingelezen, maar de inloggegevens waren onjuist. Reset de rol met sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" en plaats de identieke tekenreeks in het bestand.
pq: database "listmonk" does not exist betekent dat de database-waarde in [db] geen bestaande database aanduidt. sudo -u postgres psql -l toont wat er daadwerkelijk op de server aanwezig is, inclusief de spelling die u per ongeluk heeft gebruikt.
permission denied tijdens --install betekent dat de rol wel verbinding kan maken, maar geen eigenaar is van de database, waardoor er geen tabellen kunnen worden aangemaakt. Herstel dit met sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" en voer de installatie opnieuw uit.
De service start nooit en het logboek noemt het configuratiebestand. Een proces dat draait als listmonk kan een config.toml niet openen als deze is achtergelaten als root:root met modus 600. stat -c '%U:%G %a' /etc/listmonk/config.toml zou root:listmonk 640 moeten weergeven en de bovenliggende map zou root:listmonk 750 moeten zijn.
Het paneel werkt, maar er komt geen e-mail aan. Dat is geen opstartprobleem. Controleer eerst de instellingen en SMTP, en daarna het logboek van de campagne zelf in het beheerderspaneel; hierin wordt de fout geregistreerd die de mailserver bij elke poging teruggaf.
FAQ
Heb ik mijn eigen mailserver nodig om Listmonk te gebruiken?
Nee. Listmonk is geen mailserver. Het vereist SMTP-inloggegevens voor een server die uw e-mail accepteert en aflevert; dit kan een transactionele relay zijn of een mailserver die u zelf beheert. Voer deze inloggegevens in onder Settings en SMTP in het beheerderspaneel, niet in config.toml, aangezien de mailinstellingen in de database worden opgeslagen. Gebruik poort 587 met STARTTLS of poort 465 met impliciete TLS, aangezien de meeste VPS-providers uitgaand verkeer op poort 25 blokkeren voor nieuwe accounts.
Waarom verwijzen de links in mijn campagne naar localhost:9000?
De instelling voor de root URL staat nog op de standaardwaarde van de installatie: http://localhost:9000. Listmonk schrijft deze waarde in afmeldlinks en media-URL's op het moment dat een campagne wordt verzonden. Open Settings in het beheerderspaneel, stel de root URL in op uw werkelijke HTTPS-adres en sla de wijzigingen op. Reeds verzonden berichten kunnen niet worden gecorrigeerd; stuur daarom eerst een testcampagne naar uzelf en klik op de afmeldlink voordat u een echte mailinglijst aanschrijft.
Worden mijn abonnees verwijderd als ik --install opnieuw uitvoer?
Ja. --install is het installatieprogramma voor de eerste keer en dit verwijdert het bestaande Listmonk-schema, en --yes verwijdert de prompt die u hiervoor had gewaarschuwd. Gebruik in elk script dat mogelijk vaker wordt uitgevoerd --install --idempotent --yes; dit doet niets als de tabellen al bestaan. Om schemawijzigingen van een nieuwe release toe te passen, stopt u de service, maakt u een pg_dump en voert u vervolgens --upgrade uit.
Waarom geeft Listmonk de melding dat wachtwoordauthenticatie voor gebruiker listmonk is mislukt?
Het wachtwoord in het [db]-blok van /etc/listmonk/config.toml komt niet overeen met de Postgres-rol met dezelfde naam. De logregel is pq: password authentication failed for user "listmonk" en pq is de Postgres-driver die de afwijzing van de server doorgeeft, wat betekent dat het configuratiebestand is gevonden en gelezen. Reset het wachtwoord van de rol met sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", schrijf dezelfde reeks in het configuratiebestand en voer vervolgens sudo systemctl restart listmonk uit.