SSD Nodes Learn 8GB RAM — $66/rok
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-01

Listmonk na VPS: newsletter na Ubuntu 24.04

Instrukcja instalacji Listmonk v6.2.0 na Ubuntu 24.04 z PostgreSQL, config.toml, systemd, TLS i SMTP. Sprawdź też, ile naprawdę trwa budowanie dostarczalności.

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

Czego wymaga samodzielnie hostowany newsletter w Listmonk

Listmonk to samodzielnie hostowany newsletter i menedżer list mailingowych: jeden plik binarny Go, jedna baza danych PostgreSQL, jeden plik konfiguracyjny i jedna jednostka systemd. Niewielki VPS uruchamia go bez problemu, ponieważ Listmonk przechowuje subskrybentów i kolejkuje kampanie, ale sam nigdy nie dostarcza wiadomości. Przekazuje każdą wiadomość do serwera SMTP (simple mail transfer protocol), dlatego o współczynniku dostarczalności decyduje reputacja tego serwera, a nie tego oprogramowania.

W tym poradniku zainstalowano Listmonk v6.2.0, czyli bieżące wydanie z lipca 2026, na Ubuntu 24.04. Wymagany jest VPS z publicznym adresem IP, kontrolowana nazwa domeny oraz PostgreSQL 12 lub nowszy. Instalacja zajmuje około godziny. Budowanie reputacji nadawcy trwa tygodnie. Ten temat omówiono pod koniec poradnika.

Instalacja PostgreSQL i utworzenie bazy danych

Ubuntu 24.04 udostępnia PostgreSQL 16 we własnym repozytorium. To znacznie nowsza wersja, niż wymaga Listmonk.

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

Utwórz rolę i bazę danych w jednej sesji psql. -v ON_ERROR_STOP=1 powoduje zakończenie działania psql po pierwszej nieudanej instrukcji, dzięki czemu literówka nie pozostawi częściowo skonfigurowanego środowiska, które wygląda na gotowe.

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 nie jest opcjonalne. Instalacja schematu tworzy tabele, typy, indeksy i funkcje, dlatego rola musi być właścicielem bazy danych. Wskazanie w Listmonk bazy należącej do innej roli powoduje zatrzymanie instalacji z błędem permission denied, nawet po wykonaniu GRANT CONNECT.

Przed kontynuowaniem potwierdź, że baza danych istnieje.

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

Zostanie wyświetlony komunikat listmonk. Pusty wiersz oznacza, że instrukcja CREATE nie została wykonana. Sprawdź ponownie dane wyjściowe psql.

Instalowanie pliku binarnego Listmonk

Listmonk udostępnia statyczny plik binarny dla każdej architektury. Najpierw sprawdź architekturę systemu, ponieważ plik binarny amd64 na serwerze VPS ARM nie zostanie wykonany przez jądro.

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

Na serwerze VPS ARM zamień amd64 na arm64 w nazwie pliku. Wyświetlenie ciągu wersji przez listmonk --version jest pierwszym potwierdzeniem, że plik binarny jest zgodny z maszyną.

Wygeneruj plik config.toml i ogranicz do niego dostęp

--new-config zapisuje config.toml w bieżącym katalogu roboczym. Dlatego cd znajduje się wewnątrz sh -c, a nie przed sudo.

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

Wygenerowany plik jest krótki. W sekcji [app] address = "localhost:9000" wiąże serwer HTTP wyłącznie z interfejsem loopback, dlatego panel administracyjny nie jest dostępny z Internetu, dopóki nie zostanie skonfigurowany reverse proxy. Tej linii nie należy zmieniać. W sekcji [db] znajdują się host = "localhost", port = 5432, user = "listmonk", database = "listmonk" i ssl_mode = "disable". Te wartości domyślne są już zgodne z utworzoną bazą danych, dlatego należy zmienić tylko hasło.

ssl_mode = "disable" jest prawidłowe, gdy Postgres nasłuchuje na interfejsie loopback na tym samym komputerze, ponieważ ten ruch nie opuszcza maszyny. Po przeniesieniu bazy danych na inny host należy ustawić require, ponieważ w przeciwnym razie hasło będzie przesyłane przez sieć w postaci jawnej.

Należy edytować wiersz hasła w sekcji [db], aby był zgodny z rolą, następnie utworzyć konto usługi i odebrać dostęp do pliku wszystkim pozostałym kontom.

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

Konto usługi może teraz odczytać plik, a żadne inne konto nie może tego zrobić.

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

Pierwsze polecenie wyświetla readable. Drugie wyświetla root:listmonk 640. Każde inne nieuprzywilejowane konto, które spróbuje wykonać to samo cat, otrzyma Permission denied. Taki jest cel tego ustawienia: plik zawiera hasło do bazy danych w postaci jawnej, a serwer zwykle ma więcej niż jedno konto logowania. Ta sama zasada dotyczy każdej uruchamianej usługi, dlatego należy zapoznać się z zasadą minimalnych uprawnień kont usług i stosować ją wszędzie.

Tworzenie schematu za pomocą --install

--install tworzy tabele i inicjuje domyślne ustawienia. Pierwsze dane logowania administratora należy ustawić za pomocą zmiennych środowiskowych, aby konto istniało, zanim panel będzie dostępny.

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

--yes odpowiada na monit o potwierdzenie. Przed zautomatyzowaniem tego polecenia należy raz odczytać ten monit, ponieważ --install jest instalatorem uruchamianym po raz pierwszy i usuwa istniejący schemat Listmonk. Ponowne uruchomienie go w działającej bazie danych usuwa subskrybentów. W każdym skrypcie, który może zostać uruchomiony dwukrotnie, należy użyć --install --idempotent --yes. To polecenie nie wykonuje żadnych działań, jeśli tabele już istnieją. Zmiany schematu dostarczone w nowej wersji stosuje się za pomocą --upgrade, nigdy za pomocą --install.

Wynik należy sprawdzić po stronie bazy danych, a nie w przeglądarce.

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

Pierwsze polecenie wyświetla tabele Listmonk, w tym subscribers, lists, campaigns, templates i bounces. Drugie wyświetla admin. Pusty wynik drugiego polecenia oznacza, że zmienne środowiskowe nie zostały przekazane do procesu. W takim przypadku panel poprosi o utworzenie pierwszego użytkownika w przeglądarce.

Uruchamianie Listmonk za pomocą systemd

Wpisz /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 ma znaczenie, ponieważ Listmonk interpretuje ścieżki względne, w tym ścieżkę systemu plików używaną do przesyłania multimediów, względem tego katalogu. After=postgresql.service tylko nakazuje uruchomienie usługi. Nie czeka, aż Postgres zacznie akceptować połączenia. Dlatego Restart=on-failure obsługuje przypadek, w którym Listmonk uruchamia się nieco za wcześnie i nie może nawiązać połączenia.

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

ss powinno pokazywać 127.0.0.1:9000 w stanie LISTEN. Zwrócenie przez curl dowolnego wiersza statusu HTTP oznacza, że serwer odpowiada. Niepowodzenie curl z komunikatem Connection refused oznacza, że proces zakończył działanie podczas uruchamiania. journalctl -u listmonk -n 50 --no-pager wskaże przyczynę. Należy pamiętać, że enable --now jest elementem, który przetrwa ponowne uruchomienie systemu. Proces uruchomiony ręcznie zakończy działanie po kolejnej aktualizacji jądra.

Umieść nginx i TLS z przodu

Listmonk używa zwykłego protokołu HTTP na interfejsie loopback, dlatego nginx kończy połączenie TLS (transport layer security) i przekazuje żądanie.

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

Wartość client_max_body_size trzeba zwiększyć, ponieważ importy subskrybentów i przesyłanie plików multimedialnych korzystają z żądań zawierających pliki, a nginx domyślnie odrzuca wszystko powyżej 1 MB, zwracając 413 Request Entity Too Large. Certyfikat należy wystawić za pomocą certbot. Narzędzie to zapisze również wpisy listen 443 ssl oraz przekierowanie z portu 80. Instrukcje znajdują się w przewodniku po certyfikatach Let's Encrypt dla nginx. Należy otworzyć porty 80 i 443, a port 9000 pozostawić zamknięty, ponieważ proxy łączy się z nim przez loopback. Jeśli konfiguracja zapory sieciowej nie była jeszcze zmieniana, należy rozpocząć od podstaw konfiguracji zapory ufw.

Następnie należy otworzyć panel administracyjny i w sekcji Settings ustawić główny URL na https://lists.example.com. Nowa instalacja zawiera wartość http://localhost:9000, a Listmonk zapisuje ją w każdym odnośniku rezygnacji z subskrypcji i adresie URL pliku multimedialnego umieszczanym w wiadomości e-mail. Jeśli kampania zostanie wysłana przed zmianą tej wartości, każdy odbiorca otrzyma odnośniki wskazujące na własny komputer. Odnośniki te nie będą działać u odbiorców, a dla filtra antyspamowego będą wyglądać jak konfiguracja nadawcy, który nie potrafi skonfigurować własnej domeny.

Połącz SMTP, którego nie ma w config.toml

W pliku config.toml nie ma sekcji SMTP. Ustawienia poczty są przechowywane w bazie danych, w tabeli settings, i można je edytować w panelu administracyjnym w sekcji Settings i SMTP. Dlatego wygenerowany plik pozostaje tak krótki. Dlatego również zmiana ustawień SMTP nie wymaga ponownego uruchomienia usługi.

W przypadku samego serwera SMTP istnieją dwie uzasadnione możliwości. Można uruchomić własny serwer. Wtedy reputacja adresu IP zależy wyłącznie od użytkownika, a samo wdrożenie jest osobnym projektem: w uruchamianiu własnego serwera pocztowego z Mailcow opisano związane z tym działania. Można też skonfigurować Listmonk do korzystania z przekaźnika transakcyjnego i pozostawić zarządzanie reputacją adresu IP dostawcy tej usługi.

W obu przypadkach należy użyć portu 587 z STARTTLS albo portu 465 z niejawnym TLS. Nie należy planować wysyłania przez port 25. Większość dostawców VPS domyślnie blokuje ten port na nowych kontach. Zablokowany port 25 wygląda dokładnie jak zawieszone połączenie, ponieważ pakiety są odrzucane bez odpowiedzi, a nie jawnie odrzucane przez usługę. Klient czeka więc na przekroczenie limitu czasu zamiast szybko zgłosić błąd.

Należy wykonać test przed rozpoczęciem korzystania z konfiguracji. Trzeba utworzyć listę, dodać własny adres jako subskrybenta i wysłać kampanię do jednego odbiorcy. Następnie należy otworzyć odebraną wiadomość i odczytać pełne nagłówki. Nagłówek Authentication-Results dodany przez stronę odbierającą wskazuje, czy mechanizmy SPF i DKIM zakończyły weryfikację pomyślnie.

Dostarczalność jest najważniejszym zadaniem

Listmonk tworzy wiadomość, zarządza listą i przekazuje wiadomość do wysłania. Każda decyzja dotycząca tego, czy wiadomość dotrze do skrzynki odbiorczej, jest podejmowana przez dostawcę poczty odbiorcy na podstawie wysyłającego adresu IP i wysyłającej domeny. Nowy adres IP VPS nie ma żadnej historii, a każdy duży dostawca skrzynek pocztowych traktuje brak historii jako umiarkowanie podejrzany.

Cztery elementy są wymagane:

  • Rekord TXT SPF (sender policy framework), wskazujący hosta uprawnionego do wysyłania wiadomości w imieniu domeny.
  • Klucz DKIM (domainkeys identified mail) opublikowany jako rekord TXT; podpisywanie musi być wykonywane przez serwer pocztowy, a nie przez Listmonk.
  • Rekord DMARC (domain based message authentication, reporting and conformance), określający działanie odbiorców w przypadku niepowodzenia dwóch pierwszych mechanizmów.
  • Skrzynka pocztowa do obsługi odbić, odczytywana przez Listmonk, aby adresy odrzucające wiadomości były usuwane z listy zamiast otrzymywać kolejne próby wysyłki bez końca.

Na początku należy również wysyłać wiadomości powoli. Domena, z której wcześniej nie wysyłano poczty i która nagle dostarcza dziesięć tysięcy wiadomości w ciągu godziny, wygląda dokładnie jak domena przejętego konta, dlatego wiadomości są filtrowane tak, jakby pochodziły z takiego konta. Należy rozpocząć od najbardziej zaangażowanych subskrybentów i zwiększać wolumen w ciągu kolejnych dni.

Każdy szablon musi również zawierać działający link rezygnacji z subskrypcji. W szablonie Listmonk jest to {{ UnsubscribeURL }}, a treść kampanii jest umieszczana w miejscu {{ template "content" . }}, które musi wystąpić w każdym szablonie dokładnie raz. Kampania bez linku rezygnacji z subskrypcji powoduje zgłoszenia spamu zamiast rezygnacji z subskrypcji, a zgłoszenia są najszybszym sposobem utraty reputacji nadawcy budowanej przez wiele tygodni.

Kopie zapasowe i wymagania dotyczące przywracania

Z serwera muszą zostać przeniesione dwie rzeczy: zrzut bazy danych i config.toml. Jeśli obrazy są przesyłane do kampanii, należy dodać katalog multimediów.

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

Zrzut zawiera subskrybentów, kampanie, szablony i wszystkie ustawienia, w tym dane uwierzytelniające SMTP. Należy go zaszyfrować i przechowywać poza tym serwerem. Harmonogram można skonfigurować bez problemu: zob. zaszyfrowane kopie zapasowe restic w zdalnej pamięci masowej. config.toml zawiera kilka wierszy, ale przechowuje hasło do bazy danych, dlatego należy traktować go tak samo.

Aktualizacje należy wykonywać w określonej kolejności. Należy zatrzymać usługę, utworzyć zrzut, zastąpić plik binarny w /usr/bin, uruchomić listmonk --config /etc/listmonk/config.toml --upgrade, a następnie uruchomić usługę. Migracje schematu są wykonywane wyłącznie do nowszych wersji, dlatego ten zrzut jest jedyną drogą powrotu.

Dlaczego Listmonk nie uruchamia się?

Najpierw odczytaj dziennik za pomocą journalctl -u listmonk -n 50 --no-pager. Prawie każda awaria podczas uruchamiania jest opisana w jednym wierszu bloku [db].

pq: password authentication failed for user "listmonk" oznacza, że hasło w [db] nie jest zgodne z rolą Postgres. Prefiks pq informuje, że sterownik Postgres zgłosił odrzucenie przez serwer. Oznacza to, że konfiguracja została odczytana poprawnie, ale dane uwierzytelniające były nieprawidłowe. Zresetuj rolę za pomocą sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" i wpisz identyczny ciąg znaków do pliku.

pq: database "listmonk" does not exist oznacza, że wartość database w [db] nie wskazuje istniejącej bazy danych. sudo -u postgres psql -l wyświetla bazy znajdujące się na serwerze, w tym pisownię użytej omyłkowo nazwy.

permission denied podczas --install oznacza, że rola może się połączyć, ale nie jest właścicielem bazy danych i dlatego nie może utworzyć w niej tabel. Napraw to za pomocą sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" i ponownie uruchom instalację.

Usługa nigdy się nie uruchamia, a dziennik wskazuje plik konfiguracyjny. Proces działający jako listmonk nie może otworzyć pliku config.toml, jeśli ma on właściciela root:root i uprawnienia 600. stat -c '%U:%G %a' /etc/listmonk/config.toml powinno wyświetlić root:listmonk 640, a katalog nadrzędny powinien mieć właściciela root:listmonk 750.

Panel działa, ale poczta nie dociera. Nie jest to problem z uruchamianiem usługi. Najpierw sprawdź ustawienia i SMTP, a następnie własny dziennik kampanii w panelu administracyjnym. Zawiera on błąd zwrócony przez serwer pocztowy przy każdej próbie wysyłki.

FAQ

Czy do korzystania z Listmonk potrzebny jest własny serwer pocztowy?

Nie. Listmonk nie jest serwerem pocztowym. Wymaga danych uwierzytelniających SMTP serwera, który przyjmuje wiadomości i dostarcza je odbiorcom. Może to być przekaźnik transakcyjny lub samodzielnie zarządzany serwer pocztowy. Te dane należy skonfigurować w sekcjach Settings i SMTP w panelu administracyjnym, a nie w config.toml, ponieważ ustawienia poczty są przechowywane w bazie danych. Należy użyć portu 587 z STARTTLS lub portu 465 z niejawnym TLS, ponieważ większość dostawców VPS blokuje wychodzący port 25 na nowych kontach.

Dlaczego odnośniki w mojej kampanii prowadzą do localhost:9000?

Ustawienie głównego adresu URL nadal ma domyślną wartość instalacyjną http://localhost:9000. Listmonk zapisuje tę wartość w odnośnikach rezygnacji z subskrypcji i adresach URL multimediów w momencie wysyłania kampanii. Należy otworzyć Settings w panelu administracyjnym, ustawić główny adres URL na rzeczywisty adres HTTPS i zapisać zmiany. Dostarczonych wiadomości nie można już poprawić. Przed wysłaniem wiadomości do właściwej listy należy wysłać kampanię testową do siebie i kliknąć zawarty w niej odnośnik rezygnacji z subskrypcji.

Czy ponowne uruchomienie --install usunie moich subskrybentów?

Tak. --install jest instalatorem uruchamianym po raz pierwszy i usuwa istniejący schemat Listmonk, a --yes usuwa monit ostrzegający przed tą operacją. W każdym skrypcie, który może zostać uruchomiony dwukrotnie, należy użyć --install --idempotent --yes. Ta opcja nie wykonuje żadnej operacji, jeśli tabele już istnieją. Aby zastosować zmiany schematu w nowej wersji, należy zatrzymać usługę, wykonać pg_dump, a następnie uruchomić --upgrade.

Dlaczego Listmonk zgłasza błąd password authentication failed for user listmonk?

Hasło w bloku [db] pliku /etc/listmonk/config.toml nie jest zgodne z hasłem roli Postgres o tej samej nazwie. Wpis w dzienniku ma postać pq: password authentication failed for user "listmonk", a pq oznacza przekazanie przez sterownik Postgres odrzucenia zgłoszonego przez serwer. Oznacza to, że plik konfiguracyjny został znaleziony i odczytany. Należy zresetować hasło roli za pomocą sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", zapisać identyczny ciąg znaków w pliku konfiguracyjnym, a następnie uruchomić sudo systemctl restart listmonk.