Listmonk — darmowy mailing manager — studium przypadku i implementacja

Opublikowano Kategorie Backend, PozostałeCzas czytania 16min

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 — Sendylistmonk.

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.

Panel administracyjny Listmonk

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.

Widok listy subskrybentów listmonk

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.

Przykład szablonu wizualnego listmonk
Przykładowy szablon wizualny

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.

konfiguracja AWS SES w listmonk

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

Dominik Szczepaniak

Zawodowo Senior Software Engineer w CKSource. Prywatnie bloger, fan włoskiej kuchni, dobrej kawy i miłośnik jazdy na rowerze.

Inne wpisy, które mogą Cię zainteresować

Kolejna książka o Gicie — naucz się korzystać z Gita jak profesjonalista

Okładka e-booka - Kolejna książka o Gicie

"Kolejna książka o Gicie" to kompleksowy e-book, który pozwoli Ci poznać Gita od A do Z, a także liczne narzędzia dedykowane pracy z Gitem!

Dlaczego warto?

  • 👉 Git od podstaw do poziomu PRO. E-book przeprowadzi Cię krok po kroku, niezależnie od poziomu doświadczenia.
  • 👉 Zdobędziesz praktyczne umiejętności, które natychmiast wykorzystasz w prawdziwych projektach.
  • 👉 Wiedza połączona z praktyką! Oprócz masy teorii w e-booku znajdziesz też zadania praktyczne.

Okładka e-booka - Kolejna książka o Gicie

Przygotuj się lepiej do rozmowy o pracę!

Odbierz darmowy egzemplarz e-booka 106 Pytań Rekrutacyjnych Junior JavaScript Developer i realnie zwiększ swoje szanse na rozmowie rekrutacyjnej! Będziesz też otrzymywać wartościowe treści i powiadomienia o nowych wpisach na skrzynkę e-mail.

Dlaczego warto?

  • 👉 Ponad 1000 pobrań e-booka!
  • 👉 60 stron pełnych pytań i zadań praktycznych. Pytania i zadania pochodzą z faktycznych procesów rekrutacyjnych.

E-booka odbierzesz korzystając z formularza poniżej 👇

Okładka e-booka - Kolejna książka o Gicie

guest

2 komentarzy
Najwięcej głosów
Najnowsze Najstarsze
Jakub
Jakub
4 miesiące temu

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!