Listmonk — self-hosted newsletter i mailing list na VPS
Opublikowano: 10 kwietnia 2026 · Kategoria: Hosting
Mailchimp, ConvertKit i ActiveCampaign to wygodne platformy, ale ich koszty rosną z listą subskrybentów — 10 000 odbiorców to już kilkaset złotych miesięcznie. Listmonk to open-source platforma do newsletterów napisana w Go, działająca na PostgreSQL. Na VPS za 30 PLN/msc możesz wysyłać miliony emaili miesięcznie przez AWS SES ($0.10/1000). Ten artykuł pokazuje pełną instalację Listmonk przez Docker, konfigurację SMTP, tworzenie kampanii z szablonami HTML i automatyczne zarządzanie odsubskrypcjami.
Instalacja Docker Compose
Listmonk wymaga PostgreSQL jako bazy danych. Oficjalny docker-compose.yml startuje
oba serwisy. Po pierwszym uruchomieniu Listmonk tworzy tabele i wykonuje migracje automatycznie:
# Utwórz katalog i pobierz konfigurację
mkdir -p /opt/listmonk && cd /opt/listmonk
# docker-compose.yml
version: '3.8'
services:
listmonk:
image: listmonk/listmonk:latest
restart: unless-stopped
ports:
- "9000:9000"
environment:
- TZ=Europe/Warsaw
volumes:
- /opt/listmonk/config.toml:/listmonk/config.toml:ro
- /opt/listmonk/uploads:/listmonk/uploads
depends_on:
- postgres
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: listmonk
POSTGRES_PASSWORD: SILNE_HASLO
POSTGRES_DB: listmonk
volumes:
- /opt/listmonk/postgres:/var/lib/postgresql/data
# Uruchom (pierwsze uruchomienie — inicjalizacja bazy)
docker compose up -d
# Poczekaj ~10 sekund na inicjalizacje
docker compose logs listmonk | grep "admin user"
# Zapisz login i haslo admina z logow Konfiguracja config.toml
Plik config.toml to główna konfiguracja Listmonk. Kluczowe sekcje to połączenie z
bazą, adres aplikacji i ustawienia email:
[app] address = "0.0.0.0:9000" admin_username = "admin" # Zmien hasło przez UI Settings → General admin_password = "HASH_BCRYPT" [db] host = "postgres" port = 5432 user = "listmonk" password = "SILNE_HASLO" database = "listmonk" ssl_mode = "disable" max_open = 25 max_idle = 25 max_lifetime = "300s" # Globalne ustawienia newslettera (mozna tez z UI) [app.site_url] # Wymagane do generowania linkow wypisania sie site_url = "https://newsletter.example.com" # Upload zalacznikow [upload] provider = "filesystem" # lub "s3" filesystem.upload_path = "uploads" filesystem.upload_uri = "/uploads"
Konfiguracja SMTP — AWS SES i Postfix
Listmonk obsługuje wiele serwerów SMTP jednocześnie (w UI: Settings → SMTP). Możesz skonfigurować pulę serwerów z różnymi limitami wysyłki. AWS SES jest najtańszą opcją dla dużych wolumenów:
# Konfiguracja SMTP w UI Settings → SMTP → Add Server # AWS SES (eu-west-1) # Host: email-smtp.eu-west-1.amazonaws.com # Port: 587 # Auth: LOGIN # Username: AKIAIOSFODNN7EXAMPLE (SMTP credentials z IAM, nie access key!) # Password: (SMTP password z IAM — różny od secret access key) # From: [email protected] # Max simultaneous connections: 10 # Max messages/minute: 14 (SES default limit, zwieksz przez Support) # Postfix lokalny (dla mniejszych wolumenow) # Host: localhost # Port: 587 # Auth: PLAIN # TLS: STARTTLS # Test wyslania (z UI Settings → SMTP → Send Test Email) # Nginx reverse proxy dla Listmonk server { listen 443 ssl; server_name newsletter.example.com; location / { proxy_pass http://127.0.0.1:9000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
Tworzenie kampanii i szablonów HTML
Listmonk używa szablonów z dynamicznymi zmiennymi. Podstawowe zmienne to
{{.Subscriber.Name}},
{{.Subscriber.Email}} i
{{.UnsubscribeURL}} (link wypisania — wymagany prawnie):
<!-- Szablon HTML kampanii (Templates → Add New) -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{.Campaign.Name}}</title>
</head>
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1>Cześć, {{.Subscriber.FirstName}}!</h1>
<!-- Treść kampanii (edytowana osobno) -->
{{template "content" .}}
<hr style="margin: 32px 0;">
<p style="font-size: 12px; color: #666;">
Otrzymujesz ten email bo subskrybujesz nasz newsletter.<br>
<a href="{{.UnsubscribeURL}}">Wypisz się tutaj</a>
</p>
</body>
</html>
# Import subskrybentów przez CSV (Subscribers → Import)
# email,name,status,lists
# [email protected],"Jan Kowalski",enabled,"lista1"
# [email protected],"Anna Nowak",enabled,"lista1;lista2"
# Import przez API
curl -X POST https://newsletter.example.com/api/subscribers \
-H "Content-Type: application/json" \
-u admin:haslo \
-d '{
"email": "[email protected]",
"name": "Jan Kowalski",
"status": "enabled",
"lists": [1]
}' Bounce handling i unsubscribe
Listmonk automatycznie generuje linki wypisania się (unsubscribe) w każdym emailu (zastępuje {{.UnsubscribeURL}}). Bounce handling wymaga konfiguracji webhooks w panelu dostawcy SMTP:
# AWS SES Bounce Handling przez SNS
# 1. AWS Console → SES → Configuration Sets → Utwórz nowy
# 2. Dodaj Event Destination → SNS
# 3. Zaznacz: Bounces, Complaints (i opcjonalnie Deliveries)
# 4. Utwórz SNS Topic i Subscription → HTTPS endpoint:
# https://newsletter.example.com/webhooks/service/ses
# Listmonk automatycznie interpretuje bounce notification:
# - Hard bounce → subskrybent.status = disabled
# - Complaint (spam report) → subskrybent.status = blocklisted
# API Listmonk - przydatne endpointy
# GET /api/subscribers - lista subskrybentów
# POST /api/subscribers - dodaj subskrybenta
# GET /api/campaigns - lista kampanii
# POST /api/campaigns/[id]/status - zmień status kampanii
# GET /api/campaigns/[id]/stats - statystyki kampanii
# Przykład statystyk kampanii
curl -u admin:haslo https://newsletter.example.com/api/campaigns/1/stats
# {
# "data": {
# "sent": 10542,
# "to_send": 10542,
# "opened": 2341,
# "clicks": 892,
# "bounces": 23,
# "unsubscribes": 15
# }
# }