Przez długie lata na moim blogu za obsługę mailingu odpowiedzialny był Mailchimp. Jednak od dłuższego czasu szukałem sensownej alternatywy. Mój ostateczny wybór padł na listmonka. W tym artykule dowiesz się, dlaczego zakończyłem swoją przygodę z Mailchimpem, co zdecydowało o wyborze listmonka oraz na jakie trudności możesz napotkać w trakcie implementacji.
Czy znasz historię o tym, jak powstał drut?
Podobno Poznaniak z Krakowiakiem pokłócili się o pięciogroszówkę. Jako rodowity Wielkopolanin mógłbym stwierdzić, że 0 zł za prowadzenie mailingu to uczciwa cena. Mimo to uważam, że jeśli rozwiązanie jest dobrej jakości i wykorzystam pełnię jego możliwości, to warto za nie zapłacić.
Mailchimp w kontekście jakości od jakiegoś czasu coraz bardziej mnie rozczarowywał. Zaczęło się od mniej istotnych aspektów, takich jak tragicznie zoptymalizowany panel czy raporty zawierające inne dane w mailu, a inne w panelu. Czarę goryczy przelało wyłączenie mojej automatyzacji wysyłającej darmowy e-book dla czytelników. Niestety nie dostałem o tym żadnej informacji, przez co zorientowałem się dopiero po kilku miesiącach. Jej przywrócenie na moim planie nie było już możliwe.
Zdaję sobie sprawę, że na darmowym planie nie mam prawa mieć żadnych wymagań. Jednocześnie jakość świadczonych usług nie zachęciła mnie do przejścia na płatny plan, a brak możliwości wysyłki maila powitalnego był dla mnie nie do zaakceptowania. Obawiałem się też, że kolejne funkcje mogą zostać wyłączone na moim planie. Wszystko to spowodowało, że zacząłem rozglądać się za alternatywami.
Obecny rozmiar mojego mailingu to nieco ponad 700 osób. Oczywiście chciałbym, by ta liczba nadal rosła, przez co przy analizie alternatyw brałem pod uwagę cenniki dla przedziału 1000-2500 odbiorców. Dla takiego przedziału niezależnie od wybranego rozwiązania, roczny koszt prowadzenia mailingu przekraczałby 1000 zł. Biorąc pod uwagę, że mailing służy mi głównie do wysyłania informacji o nowych treściach, taka kwota to dla mnie zbyt dużo.
Dodatkowo większość gotowych narzędzi oferuje niepotrzebne mi rozwiązania. Mam tu na myśli asystenty AI, kreatory landing pages czy integracje z mediami społecznościowymi. Nie widzę sensu, by płacić za usługi, z których nie korzystam.
Mailingi self-hosted
Szukając alternatyw, mój wzrok skierował się w stronę rozwiązań self-hosted. Analizując dostępne możliwości, znalazłem dwa sensowne rozwiązania — Sendy i listmonk.
Sendy jest rozwiązaniem obecnym na rynku już kilkanaście lat i jest wykorzystywane do prowadzenia nawet dużych newsletterów, takich jak #unknowNews. Koszt prowadzenia newslettera z Sendy jest bardzo niski w porównaniu z wcześniej rozważanymi alternatywami. Sendy kosztuje jednorazowo 69 USD oraz wymaga podpięcia usługi AWS SES do wysyłki maili (0.10 USD za 1000 maili). By jednak być uczciwym, Sendy to nie jest zaawansowany produkt, co zresztą przyznaje twórca wcześniej wspomnianego newslettera:
Sendy ma jeden ogromny plus. Używa pod spodem skrajnie taniego AWS SES. Wychodzi jakieś 40gr za wysłanie 1000 wiadomości.
Mój newsletter powoli zbliża się do 17 tysięcy subskrybentów. To liczba, gdzie za prowadzenie newslettera w profesjonalnych firmach płaci się już dziesiątki tysięcy złotych rocznie. Dzięki Sendy mogę utrzymać koszty na poziomie kilkudziesięciu złotych miesięcznie.
Na tym niestety zalety Sendy się kończą. Nie jest to wygodny soft i brakuje mu wielu rzeczy, głównie związanych z automatyzacją. Plusem jest jednak to, że to soft selfhosted, więc wszystko, co potrzebowałem, to sobie sam dopisałem.
Do kosztów mailingu należy więc doliczyć czas niezbędny na dodanie brakujących funkcji i późniejsze utrzymanie tego.
Alternatywą dla Sendy jest listmonk. To darmowy mailing list manager napisany w Go i z otwartymi źródłami. Lista kontrybutorów jest dość spora, jednak w praktyce rozwija go jedna osoba. Jeśli chcesz przetestować listmonka bez konieczności samodzielnego uruchomienia, to możesz skorzystać z publicznego demo. Mimo że byłem już gotowy, by postawić na Sendy, zdecydowałem się dać wypróbować listmonka. Po przetestowaniu rozwiązania zdecydowałem, że przenoszę się całkowicie na to rozwiązanie.
W przypadku mailingu self-hosted dodatkowym kosztem jest serwer, na którym uruchomimy usługę. Można istotnie ograniczyć ten koszt, decydując się np. na Mikr.usa — tani VPS, który w zupełności do tego celu wystarczy.
Listmonk — implementacja
Jedyną zależnością konieczną do uruchomienia listmonka jest Postgres. Dla Postgresa wymagane jest rozszerzenie pgcrypto, o czym brak informacji w dokumentacji. Jeśli zamierzasz korzystać z bazy danych zarządzanej przez kogoś innego, upewnij się, że pgcrypto jest tam zainstalowane lub że jest taka możliwość. Listmonka można zainstalować z binarki lub postawić w formie kontenera wykorzystując np. Dockera. Ja zdecydowałem się na ten drugi wariant. W połączeniu z setupem dla Postgresa powstał gotowy plik Docker Compose.
version: "3.9"
services:
postgres:
image: postgres:17-alpine
container_name: listmonk_postgres
env_file: .env
environment:
POSTGRES_DB: $POSTGRES_DB
POSTGRES_USER: $POSTGRES_USER
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
volumes:
- ./postgres/data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- internal
listmonk:
image: listmonk/listmonk:v6.0.0
container_name: listmonk
depends_on:
postgres:
condition: service_healthy
volumes:
- ./listmonk/config.toml:/listmonk/config.toml
- ./listmonk/uploads:/listmonk/uploads
command: >
sh -c "
./listmonk --install --idempotent --yes --config /listmonk/config.toml &&
./listmonk --upgrade --yes --config /listmonk/config.toml &&
./listmonk --config /listmonk/config.toml
"
ports:
- "127.0.0.1:9000:9000"
restart: unless-stopped
networks:
- internal
networks:
internal:
driver: bridge
Po uruchomieniu pliku i przejściu pod http://localhost:9000 zostaniemy przekierowani do panelu administracyjnego Listmonka.

W przedstawionym setupie warto zwrócić uwagę na kilka fragmentów. Przed produkcyjnym uruchomieniem rekomenduję ręczne ustawienie wersji obrazu listmonk na najnowszą.
Drugim elementem, na który chciałbym zwrócić uwagę, jest volume wskazujący na katalog uploads. Wskazuje on na lokalizację, do której uploadowane są pliki wykorzystywane potem w mailach. W analogiczny sposób można również podpiąć katalog z własnymi szablonami np. dla formularzy zapisu.
W produkcyjnym setupie należy zwrócić uwagę na kilka dodatkowych elementów. Pierwszym jest backup bazy danych. Wykorzystanie volume sprawia, że zawartość bazy przetrwa, jeśli naszemu kontener zostanie wyłączony. Aby mieć całkowitą pewność, że nie stracimy danych, warto zadbać o backup. Poniżej znajdziesz przykładowy skrypt tworzący dump bazy danych.
#!/bin/bash
set -euo pipefail
CONTAINER="postgres"
DB="db_name"
USER="user"
BACKUP_DIR="/var/backups/postgres"
DATE=$(date +"%Y-%m-%d_%H-%M-%S")
FILE="$BACKUP_DIR/${DB}_${DATE}.dump"
docker exec \
-e PGPASSWORD=password \
"$CONTAINER" \
pg_dump \
-U "$USER" \
-d "$DB" \
-F c \
--clean \
--if-exists \
--no-owner \
--no-acl \
> "$FILE"
echo "Backup completed: $FILE"
Uruchamianie tego skryptu możesz zautomatyzować, wykorzystując crona. Warto również zautomatyzować wysyłkę backupów do innej lokalizacji, by w razie awarii serwera nie utracić danych.
Wystawiając usługę na świat, ostatnim potrzebnym elementem jest reverse proxy. W moim przypadku jest to NGINX. Poniżej znajdziesz przykładową konfigurację.
server {
listen 80;
listen [::]:80;
server_name example.com;
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;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Listmonk — możliwości
Listmonk (podobnie jak Sendy) nie jest kombajnem marketingowym. Dostarcza minimum opcji koniecznych do prowadzenia prostego mailingu, takiego jak ten na tym blogu. Aplikacja umożliwia tworzenie listy i zapisywanie do nich subskrybentów. Dzięki temu można za pomocą jednej instancji zarządzać kilkoma projektami czy dzielić subskrybentów na segmenty.
Listmonk dostarcza bardzo proste narzędzia analityczne, Jednak nie dostarczają one konkretnych szczegółów kto i kiedy otworzył daną wiadomość. Dowiemy się jedynie, ile osób zapoznało się z naszą wiadomością i ile kliknęło linki.

Każdy subskrybent może mieć zestaw atrybutów, dzięki czemu można zapisywać dodatkowe informacje. Możemy je np. wykorzystać w automatyzacjach czy zapisać skąd dany użytkownik przyszedł. Zapis może odbywać się przez formularz na dowolnej stronie WWW lub przez dedykowaną podstronę.
Listmonk umożliwia tworzenie kampanii na podstawie wcześniej utworzonych szablonów. Szablony można tworzyć w trzech trybach: HTML, trybie edytora wizualnego i formie maila transakcyjnego. Te ostatnie to jednorazowe wiadomości wysyłane do konkretnych subskrybentów.

Ostatnią funkcją wartą opisania jest HTTP API pozwalające na interakcję z listmonkiem z poziomu klientów HTTP czy innych aplikacji.
Mając już omówione spektrum najciekawszych możliwości listmonka czas omówić braki, a trochę ich znalazłem. Jednak zanim zacznę mówić o brakach, chcę przypomnieć, że jest to darmowy projekt, rozwijany w dużej mierze przez jedną osobę. Chodzi mi tu jedynie o wskazanie czego brakuje, a nie marudzenie i narzekanie. Decydując się na takie rozwiązanie, warto mieć na uwadze, że prędzej czy później trafimy na ograniczenia lub bugi, których czas naprawy to będzie „w grudniu po południu”. Jeśli prowadzimy duży i poważny biznes, to czas poświęcony na samodzielne poprawianie narzędzi to mniej czasu na rozwiązywanie problemów klientów.
Zdecydowanie najbardziej zauważalnym brakiem jest brak automatyzacji. Nie jest to jednak nic zaskakującego. Byłby to prawdopodobnie największy i najbardziej złożony moduł całej aplikacji. Już samo określenie, co dokładnie powinno dać się automatyzować, stanowi spore wyzwanie. Znalezienie rozwiązania, które zadowoli większość użytkowników, uważam za niemożliwe.
Potrzeby związane z automatyzacją można realizować np. za pomocą Make, Zapier czy n8n. W listmonku niestety brakuje mechanizmu webhooków, co znacznie ułatwiłoby samodzielne tworzenie automatyzacji. Istnieje jednak spora szansa, że te zostaną dodane do aplikacji w niedalekiej przyszłości:
I don’t want to hardcode a specific „welcome e-mail” feature (UI/UX and logic in the backend). The extensible approach is to have an events system internally, which can then have an autoreply (or other actions like external webhooks) UX on the frontend. That gives the choice to send not just a welcome e-mail, but respond with custom templates to various scenarios.
W narzędziu zdarzają się również mniejsze błędy czy niezrozumiałe decyzje. Irytującym błędem był brak możliwości dokończenia kampanii, która w wyniku mojego przeoczenia zakończyła się w połowie błędem. Na szczęście z wykorzystaniem logów udało się powtórzyć kampanię i dosłać wiadomości reszcie subskrybentów. Przykładem niezrozumiałej przeze mnie decyzji jest brak możliwości użycia UnsubscribeURL w transactional templates. Na szczęście znalazłem sposób, by obejść ten problem.
Kolejny problem to już wspomniany brak szczegółowych statystyk dotyczących otwierania i klikalności. Przez to nie jest możliwa np. detekcja nieaktywnych subskrybentów.
Funkcją, która była dla mnie na tyle oczywista, że nawet nie sprawdziłem, czy istnieje, jest wysyłanie wiadomości powitalnej. Okazało się jednak, że jej nie ma. Skłoniło mnie to do samodzielnej eksploracji możliwości zautomatyzowania tego procesu.
n8n + Mikr.us do automatyzacji
Do przygotowania automatyzacji postanowiłem wykorzystać n8n. Decydując się na wersję Community Edition w wariancie self-hosted jedynym kosztem, jaki ponosimy to koszt wdrożenia i opłacenie serwera, na którym stoi aplikacja.
Ponieważ korzystam z VPS od Mikr.usa, skorzystałem z gotowca pozwalającego uruchomić n8n na moim serwerze z wykorzystaniem zaledwie jednej komendy. Po wywołaniu polecenia n8n_install na twoim VPS zostanie utworzona gotowa do działania instancja n8n. Stawiając usługę, upewnij się, że twoja maszyna będzie w stanie uruchomić tę usługę. Do n8n rekomendowany jest Mikrus 3.0 lub wyższy.
Workflow do wysyłki maila powitalnego uruchamiany jest w określonym interwale czasowym (Schedule Trigger). Pierwszym krokiem jest pobranie listy subskrybentów za pomocą HTTP API. Następnie odfiltrowuję wszystkich, którzy już otrzymali mail powitalny. Dla każdego nowego subskrybenta wysyłam przygotowaną wiadomość (transactional mail), a następnie zapisuję tę informację w atrybutach subskrybenta.
Jeśli korzystasz z Mikrusa, najprawdopodobniej wykorzystujesz Cloudflare do wystawiania usług. Tu jednak czycha mała pułapka. Zapytania wysyłane przez n8n są blokowane przez Bot Fight Mode. Próbowałem zdefiniować Security Rules pozwalające na dostęp z n8n, jednak bez większego sukcesu. Absolutnie odradzam również wyłączanie Bot Fight Mode. W moim przypadku po kilkunastu minutach skończyło się to tak dużym spamem, że VPS całkowicie się położył. Problem rozwiązałem, łącząc kontenery z listmonkiem i n8n w jedną sieć. Dzięki temu uderzam bezpośrednio do kontenera z pominięciem ruchu przez Cloudflare.
Amazon Simple Email Service
Ostatnim niezbędnym krokiem jest integracja z serwisem do wysyłki maili. Oczywiście możemy próbować wysyłać maile na własną rękę. Jednak z uwagi na naszą niską wiarygodność jako dostawca maili ryzyko trafienia do spamu jest dość duże. Problem ten rozwiązują usługi do wysyłki emaili.
Jedną z bardziej opłacalnych jest AWS SES. Koszt wysyłki 1000 wiadomości to 0.10 USD. Jest to koszt pomijalny nawet w przypadku zdecydowanie większych mailingów niż mój.
By zintegrować listmonka z AWS SES, będziemy potrzebować adres e-mail, domenę lub subdomenę, z których będziemy wysyłać e-maile. W tym celu należy utworzyć identity. W sekcji Configuration wybierz opcję Identities, a następnie Create identity. Podaj swoją domenę i wybierz opcję Easy DKIM oraz rekomendowaną długość klucza podpisującego. Następnie, w konfiguracji DNS swojej domeny dodaj rekordy CNAME dostarczone przez AWS. Po weryfikacji domeny przez AWS w panelu pojawi się stosowna informacja. W moim przypadku nastąpiło to niemal natychmiast.
Kolejnym krokiem jest pozyskanie SMTP Credentials. Z sekcji po lewej stronie wybierz SMTP Settings, a następnie Create SMTP credentials. Po utworzeniu użytkownika otrzymamy SMTP credentials, które należy przekazać do listmonka. W sekcji Settings/SMTP przekazujemy otrzymane wartość oraz definiujemy port. Gdyby port 465 nie zadziałał, spróbuj skorzystać z 2465. Na zakończenie dokonaj testu połączenia.

Przy okazji polecam również sprawdzić limity, które mamy na SES oraz konfigurację listmonka. W momencie publikacji artykułu, w przypadku uderzenia w rate limit nie ma możliwości powtórzenia kampanii.
Jeśli nie masz dostępu do produkcyjnego do SES-a, czeka Cię proces uzyskania akceptacji od AWS-a. W tym celu konieczne jest wysłanie wiadomości, gdzie opiszesz swój przypadek. Zanim otrzymasz dostęp produkcyjny, będziesz w stanie wysyłać wiadomości jedynie do zweryfikowanych adresów i domen w limicie 200 wiadomości dziennie.
Uzyskanie produkcyjnego dostępu nie jest jednak takie proste. Za pierwszym razem mój ogólny opis działalności niestety nie wystarczył. W przypadku odrzucenia nie otrzymamy żadnych informacji o powodach tej decyzji. Po szybkim researchu okazało się, że nie jest to rzadki problem. Całkowicie rozumiem decyzję AWS-a. Restrykcyjny proces akceptacji użytkowników sprawia, że ryzyko utraty reputacji przez AWS-a jest mniejsze, co działa na korzyść już płacących klientów.
Podjąłem drugą próbę, gdzie znacznie bardziej szczegółowo opisałem kim jestem, czym się zajmuję i jak będę wykorzystywał SES-a. Drugie podejście okazało się sukcesem. Ciężko mi stwierdzić co konkretnie sprawiło, że moja prośba została zaakceptowana. Bazując na źródłach internetowych i wytycznych AWS-a, przygotowałem poradnik, co należy opisać, by zwiększyć szanse na uzyskanie produkcyjnego dostępu:
- Opisz przypadek użycia — do czego konkretnie chcesz wykorzystać SES-a, rodzaje maili i jaką treść będą zawierać. Im bardziej szczegółowo opiszesz czym się zajmujesz i co będziesz wysyłać, tym lepiej;
- Opisz skalę i częstotliwość wysyłki maili;
- Jak pozyskujesz adresy e-mail np. czy jest to formularz zapisu, utworzenie konta w systemie;
- Czy jest możliwe wypisanie się z listy, jak zamierzasz obsługiwać bounces i complaints. Jak zamierzasz dbać o dobrą reputację nadawcy;
- Czy zostało zrobione coś poza weryfikacją domeny, w szczególności sprawdzane jest, czy wykorzystany został sandbox do wysyłki testowych wiadomości;
- Czy masz poprawnie skonfigurowane SPF, DKIM, DMARC;
- Konta z czasem życia krótszym niż rok są obdarzone obniżonym zaufaniem;
- Domeny z krótkim czasem życia nie wzbudzają zaufania AWS-a;
- Dodatkowym atutem jest wykorzystanie innych usług AWS-a, szczególnie jeśli mamy już na koncie jakieś opłacone faktury.
Po uzyskaniu dostępu produkcyjnego do SES-a warto skonfigurować automatyczną obsługę bounces i complaints. Dzięki temu nie będziemy ryzykować obniżeniem naszej reputacji, a w konsekwencji zawieszeniem dostępu do usługi.
Po tym kroku pozostaje nam rozpocząć pracę nad pierwszą kampanią!
Podsumowanie
Listmonk to świetne narzędzie, jeśli potrzebujesz prostego systemu do obsługi nieskomplikowanego mailingu. Nie rekomendowałbym go osobom oczekującym gotowego rozwiązania działającego out-of-the-box. Jednak przy odrobinie czasu i determinacji można stworzyć z niego sensowny system mailingowy, nie ponosząc przy tym gigantycznych kosztów. Mam nadzieję, że za jakiś czas z czystym sumieniem nadal będę mógł podpisać się pod tymi słowami. Jeśli jednak okaże się inaczej, swój wzrok prawdopodobnie skieruję ku Sendy.
Na zakończenie tego artykułu nie pozostaje mi nic innego jak zachęcić Cię do samodzielnego sprawdzenia listmonka oraz zapisu na mój mailing Możesz to zrobić korzystając z dedykowanej strony lub formularza na końcu tego artykułu.
Źródła i materiały dodatkowe
- rameerez – Try this free newsletter service (free Mailchimp alternative)
- rameerez – Mailchimp pricing is out of control
- 4rkal – How to setup a selfhosted newsletter using listmonk
- Yasoob Khalid – Setting up listmonk, an open-source newsletter & mailing list manager
- listmonk – Free and open source self-hosted newsletter, mailing list manager, and transactional mails
- listmonk – publiczne demo
- listmonk – GitHub
- listmonk – example Docker Compose
- NGINX Reverse Proxy
- Maximilian Kohler – listmonk setup and usage guide
- Listmonk – HTTP API docs
- Zarządzanie usługą N8N na Mikrusie
- n8n – GitHub
- n8n – Docs
- AWS Docs – What is Amazon SES?
- AWS SES – pricing
- AWS SES – Request production access (Moving out of the Amazon SES sandbox)
- AWS SES – Maintaining a positive sender reputation
- AWS SES – Email program success metrics
- Reddit – AWS SES Production Access
- Reddit – Trying to get AWS SES email limit increase
- Being Rejected for SES without a valid reason




Warto wiedzieć, że Listmonk jest dostępny również na Pikapods, gdzie za niewielką kwotę (kilka dolarów miesięcznie) możemy uzyskać hosting narzędzia, bez konieczności samodzielnego stawiania i utrzymywania. Taki kompromis między SaaS a self-hostem 🙂
Samego Listmonka nie testowałem, ale zapisuję sobie na przyszłość, możliwe, że skorzystam. Dzięki!
Jak faktycznie idzie się zmieścić w $1.5 (+ VAT) miesięcznie to jeszcze większy #cebuladeal niż ten, który ja opisałem. Do tego odchodzą koszty utrzymania. Brzmi świetnie! Dzięki za ten komentarz, przypinam dla potomnych ❤️