Skip to main content

Self-Hosting Guide: Deploy Listmonk for Email Newsletters

·OSSAlt Team
listmonkemailself-hostingdockerguide

Self-Hosting Guide: Deploy Listmonk for Email Newsletters

Listmonk is the fastest open source newsletter tool — a single Go binary that can send 100,000+ emails per hour. Self-hosting replaces Mailchimp, ConvertKit, and Buttondown at a fraction of the cost. No per-subscriber pricing.

Requirements

  • VPS with 512 MB RAM minimum
  • Docker and Docker Compose
  • Domain name (e.g., mail.yourdomain.com)
  • SMTP service (Amazon SES, Resend, SendGrid)
  • DNS access for SPF/DKIM/DMARC records
  • 5+ GB disk

Step 1: Create Docker Compose

# docker-compose.yml
services:
  listmonk:
    image: listmonk/listmonk:latest
    container_name: listmonk
    restart: unless-stopped
    ports:
      - "9000:9000"
    volumes:
      - ./config.toml:/listmonk/config.toml
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    container_name: listmonk-db
    restart: unless-stopped
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=listmonk
      - POSTGRES_USER=listmonk
      - POSTGRES_PASSWORD=your-strong-password

volumes:
  postgres_data:

Step 2: Create Configuration

Create config.toml:

[app]
address = "0.0.0.0:9000"
admin_username = "admin"
admin_password = "your-admin-password"

[db]
host = "db"
port = 5432
user = "listmonk"
password = "your-strong-password"
database = "listmonk"
ssl_mode = "disable"
max_open = 25
max_idle = 25
max_lifetime = "300s"

Step 3: Initialize Database

# Start database first
docker compose up -d db

# Run database setup
docker compose run --rm listmonk ./listmonk --install

# Start Listmonk
docker compose up -d

Step 4: Reverse Proxy (Caddy)

# /etc/caddy/Caddyfile
mail.yourdomain.com {
    reverse_proxy localhost:9000
}
sudo systemctl restart caddy

Step 5: Configure SMTP

In SettingsSMTP:

ProviderHostPortAuth
Amazon SESemail-smtp.us-east-1.amazonaws.com587SMTP credentials
Resendsmtp.resend.com587API key
SendGridsmtp.sendgrid.net587API key
Mailgunsmtp.mailgun.org587API key
Brevosmtp-relay.brevo.com587API key

SMTP configuration in Listmonk:

  1. SettingsSMTPAdd new
  2. Enter host, port, auth type, credentials
  3. Set max connections (5-10 for most providers)
  4. Set retries and wait time
  5. Test the connection

Cost comparison for 50K emails/month:

ProviderPrice
Amazon SES$5
Resend$20
SendGrid$15
BrevoFree (300/day)
Mailgun$35

Step 6: Set Up DNS Records

Required for deliverability:

# SPF — authorize your SMTP provider
yourdomain.com  TXT  "v=spf1 include:amazonses.com ~all"

# DKIM — sign your emails (get from SMTP provider)
selector._domainkey.yourdomain.com  TXT  "v=DKIM1; k=rsa; p=..."

# DMARC — tell receivers what to do with failed auth
_dmarc.yourdomain.com  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"

# Return-Path (optional but recommended)
bounce.yourdomain.com  CNAME  feedback-smtp.us-east-1.amazonses.com

Step 7: Create Subscriber Lists

  1. ListsCreate new
  2. Create lists like:
    • Newsletter (public, double opt-in)
    • Product Updates (public, double opt-in)
    • Internal (private, single opt-in)

Import subscribers:

  1. SubscribersImport
  2. Upload CSV with columns: email, name, list
  3. Map fields and import

Step 8: Create Email Templates

Listmonk uses Go templates:

{{ template "header" . }}

<h1>{{ .Campaign.Subject }}</h1>

<p>Hey {{ .Subscriber.FirstName }},</p>

{{ .Campaign.Body }}

<p>
  <a href="{{ .Campaign.URL }}">Read more →</a>
</p>

{{ template "footer" . }}

Template variables:

VariableValue
{{ .Subscriber.Email }}Subscriber email
{{ .Subscriber.FirstName }}First name
{{ .Subscriber.LastName }}Last name
{{ .Campaign.Subject }}Campaign subject
{{ .Campaign.Body }}Campaign content
{{ .UnsubscribeURL }}Unsubscribe link

Step 9: Create and Send Campaigns

  1. CampaignsCreate new
  2. Write content using the rich text editor or HTML
  3. Select target lists
  4. Preview and test (send test email to yourself)
  5. Schedule or send immediately

Campaign types:

  • Regular — one-time send
  • Optin — double opt-in confirmation

Step 10: Add Subscription Forms

Embed form on your website:

<form method="post" action="https://mail.yourdomain.com/subscription/form">
  <input type="hidden" name="nonce" />
  <input type="email" name="email" required placeholder="your@email.com" />
  <input type="text" name="name" placeholder="Your name" />
  <input type="hidden" name="l" value="YOUR_LIST_UUID" />
  <button type="submit">Subscribe</button>
</form>

API subscription:

curl -X POST 'https://mail.yourdomain.com/api/subscribers' \
  -u 'admin:your-admin-password' \
  -H 'Content-Type: application/json' \
  --data '{
    "email": "user@example.com",
    "name": "User Name",
    "lists": [1],
    "status": "enabled"
  }'

Production Hardening

Performance tuning:

[app]
concurrency = 10           # Concurrent campaign workers
message_rate = 100         # Emails per second
batch_size = 1000          # Batch size per worker

Backups:

# Database backup (daily cron)
docker exec listmonk-db pg_dump -U listmonk listmonk > /backups/listmonk-$(date +%Y%m%d).sql

Updates:

docker compose pull
docker compose up -d

Monitoring:

  • Monitor port 9000 with Uptime Kuma
  • Track bounce rates (keep under 2%)
  • Monitor SMTP queue depth
  • Check DMARC reports for delivery issues

Resource Usage

SubscribersRAMCPUDisk
1-10K256 MB1 core2 GB
10K-100K512 MB2 cores5 GB
100K-1M1 GB4 cores20 GB

Listmonk is extraordinarily lightweight — the Go binary uses minimal resources.

VPS Recommendations

ProviderSpecPrice
Hetzner2 vCPU, 2 GB RAM€4.50/month
DigitalOcean1 vCPU, 1 GB RAM$6/month
Linode1 vCPU, 1 GB RAM$5/month

Total cost: VPS ($5) + SMTP ($5 SES) = $10/month for 50K+ emails. Mailchimp would charge $299/month for 50K subscribers.


Compare email newsletter tools on OSSAlt — features, deliverability, and pricing side by side.