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: newsletter@example.com
# 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
# jan@example.com,"Jan Kowalski",enabled,"lista1"
# anna@example.com,"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": "jan@example.com",
"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
# }
# }