Listmonk is a fast, self-hosted newsletter and mailing list manager: a single Go binary plus PostgreSQL. It handles millions of subscribers, with lists, segmentation, templates, campaigns, transactional messages via API, and basic analytics. Pair it with an inexpensive SMTP provider, and you get Mailchimp-like features at a fraction of the cost, with your subscriber data on your own server.
How it fits together
Listmonk (your VPS) ──SMTP──▶ email provider (SES, Postmark, Mailgun…) ──▶ inboxes
│
PostgreSQL (subscribers, campaigns, stats)
Listmonk manages lists and campaigns. Sending is best done through a reputable SMTP or transactional provider, rather than directly from your VPS’s IP, especially for bulk volumes. See transactional email services.
Install with Docker
mkdir /opt/listmonk && cd /opt/listmonk
curl -LO https://github.com/knadh/listmonk/raw/master/docker-compose.yml
Edit docker-compose.yml (or its environment section) to set strong database credentials and the public root URL, and bind the app port to 127.0.0.1:9000. Then initialise and start:
docker compose run --rm app ./listmonk --install # creates the schema (first time only)
docker compose up -d
Open the admin UI, and create the admin account (recent versions ask on first visit).
HTTPS with Nginx
server {
server_name lists.example.com;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# listen 443 ssl; certbot --nginx -d lists.example.com
}
Set Settings → General → Root URL to https://lists.example.com. It’s used in unsubscribe and tracking links. See Let’s Encrypt.
Configure SMTP
Settings → SMTP: add one or more servers (host, port 587 or 465, username and password), set max connections and sending rate to stay within provider limits, and send a test. Listmonk can rotate across several SMTP servers. See SMTP ports.
Authenticate your sending domain at the provider: SPF, DKIM and a DMARC record. That’s mandatory for bulk sending to Gmail and Yahoo. See SPF, DKIM and DMARC.
Lists, subscribers and opt-in
- Lists: public (subscribable via forms) or private, single or double opt-in. Use double opt-in for newsletters. It keeps lists clean, and proves consent.
- Import subscribers from CSV (with attributes as JSON), only people who actually agreed to receive mail.
- Segmentation: query subscribers with SQL expressions on attributes (
subscribers.attribs->>'plan' = 'pro').
Subscription forms: Settings → Subscription forms generates HTML to embed on your website. A public archive page and a preferences page are built in.
Campaigns and templates
- Templates: HTML templates with Go templating (
{{ .Subscriber.FirstName }}), plus Markdown or rich-text campaign bodies. - Campaigns: choose lists, subject and from address, content, then send a test, schedule or send now.
- Analytics: views (tracking pixel) and clicks (link tracking), both optional, for privacy.
The transactional API sends single templated messages (password resets, receipts) through the same system.
Bounces and complaints
Configure bounce processing (Settings → Bounces): via a POP3 mailbox, or provider webhooks (SES, SendGrid, Postmark), so hard-bounced and complaining addresses are blocklisted automatically. Sending to dead addresses damages your sender reputation quickly.
Deliverability and compliance checklist
- One-click unsubscribe headers (Listmonk adds
List-Unsubscribe), and a visible unsubscribe link. - Physical address and sender identity, as required by anti-spam laws (CAN-SPAM, GDPR, and so on).
- Warm up new sending domains and IPs gradually.
- Keep complaint rates very low, and remove inactive subscribers periodically.
See email deliverability.
Backups
The PostgreSQL database holds everything: subscribers, campaigns and settings.
docker compose exec -T db pg_dump -U listmonk listmonk | gzip > /backups/listmonk-$(date +%F).sql.gz
Subscriber lists contain personal data. Encrypt backups. See GPG encryption and backup strategy.
Quick reference
| Task | Command / place |
|---|---|
| Install schema | ./listmonk --install |
| Upgrade schema | ./listmonk --upgrade (after updating the image) |
| Admin UI | port 9000 behind Nginx |
| SMTP | Settings → SMTP |
| Forms | Settings → Subscription forms |
| Bounces | Settings → Bounces |
Related: MX records and IP blacklists.