Skip to main content

Overview

Listmonk is a high-performance, self-hosted newsletter and mailing list manager distributed as a single Go binary — a self-hosted alternative to Mailchimp, Sendy, and Mautic. This template deploys the listmonk server on port 9000 — admin dashboard, campaign engine, public subscription and tracking pages, and the transactional-mail API — backed by PostgreSQL, with the database schema install and Super Admin bootstrap handled automatically on first boot.

Architecture

  • Listmonk — A stateful, single-replica workload serving the admin UI, the public subscription/tracking pages, and the HTTP API on port 9000. The container boots through listmonk’s own idempotent install chain: it waits for the database, installs the schema, applies any pending migrations, then starts the server. A default install is ready in about a minute with no manual setup step, and a restart re-runs the same chain safely — it detects an already-provisioned database, skips the install, and leaves existing data untouched.
  • PostgreSQL (single-instance, default) — The postgres template as a subchart: the store for all lists, subscribers, campaigns, templates, and settings.
  • PostgreSQL (HA, optional) — The postgres-highly-available template instead: 3× Patroni PostgreSQL with automatic failover, fronted by an HAProxy leader endpoint that listmonk connects through for writes and schema migrations.

What Gets Created

  • Stateful Listmonk Workload — The listmonk server on port 9000, one replica, with configurable CPU and memory.
  • Database Workloads — Single mode: one stateful PostgreSQL workload. HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload.
  • Volume Sets — A persistent uploads volume set mounted at /listmonk/uploads for media uploaded through the admin UI, plus the database subchart’s data volumes (10 GiB by default; per replica in HA mode).
  • Secret — A dictionary secret holding the admin bootstrap username and password. The database credentials secret is created by the backing store subchart.
  • Identity & Policy — An identity bound to the listmonk workload, with a policy granting reveal on exactly two secrets: the admin bootstrap secret and the active backing store’s credentials secret.
  • Cron Backup Workload (optional) — Created in the backing PostgreSQL store when database backups are enabled.
This template does not create a GVC. You must deploy it into an existing GVC.

Prerequisites

None for a default install — it deploys with a working PostgreSQL backend, an installed schema, a bootstrapped admin account, and an auto-assigned HTTPS endpoint. Two things are configured after install rather than at install time:
  • SMTP — required before any mail is delivered. Configured in listmonk’s own admin UI (see Post-Install Setup).
  • Database backups (optional) — need a bucket and provider access set up beforehand (see Backing Up).
Install the template using your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Choosing a Database Mode

Exactly one of the two backing stores must be enabled — the chart enforces this at render. Listmonk is wired to the active database automatically, including for the first-boot schema install. To switch to HA mode, set postgres.enabled: false and postgresHA.enabled: true.

Configuration

Key configuration values (see the template’s values.yaml for the complete set):

Application

  • image — The official listmonk image from Docker Hub.
  • resources — CPU and memory bounds for the listmonk workload. Listmonk is a single Go binary, so the defaults suit typical installs.
  • volumeset.capacity — Initial size in GiB (minimum 10) of the uploads volume set mounted at /listmonk/uploads. It holds images and files uploaded through the admin Media page, including generated thumbnails, and survives restarts and redeploys.
  • timezone — The container timezone, which governs the times used for scheduled campaigns.

Admin Bootstrap

These become the Super Admin account created during the first boot’s schema install, and are stored in the template-managed {release}-listmonk-admin dictionary secret. The chart rejects a username shorter than 3 characters or a password shorter than 8 at render time, before anything is deployed.
The Super Admin is created on the first install only. Afterwards the account lives in the database, and changing admin.username / admin.password in values does not update it. Set a strong password before you install; manage users afterwards in Admin → Settings → Users.

Access

  • publicAccess.enabled — Serve listmonk on the auto-assigned *.cpln.app HTTPS canonical endpoint (default). This is what makes the subscriber-facing pages — subscription forms, unsubscribe links, and tracking pixels — reachable from the internet. The admin UI and admin API on the same endpoint remain authentication-gated. Set to false for an internal-only instance: external requests are then refused at the edge, and in-GVC callers still reach it per internalAccess.
  • internalAccess.type — Controls which workloads can reach listmonk over the internal network:
Firewall changes take up to a couple of minutes to propagate after a helm upgrade reports success.

Backing Store

Enable exactly one of postgres (single-instance, default) or postgresHA (HA) — see Choosing a Database Mode. In both modes, change the database password before installing (postgres.config.password / postgresHA.postgres.password); it seeds the database on first boot and cannot be changed by editing values afterwards. The database holds everything except uploaded media: lists, subscribers, campaigns, templates, users, and all of the settings you configure in the admin UI.

Connecting

Once the workload reports ready, open https://<canonical>.cpln.app/admin and sign in with the bootstrap credentials — the schema is already installed and the Super Admin already exists, so there is no setup wizard and no manual install step.

Post-Install Setup

Mail delivery and object-storage media are listmonk settings stored in its database, not template values. Configure them in the admin UI after installing:
1

Configure SMTP

In Admin → Settings → SMTP, add your mail provider (Amazon SES, SendGrid, Mailgun, Postmark, or any SMTP relay) and save. Until a working SMTP server is configured, campaigns still run to completion but no mail is delivered.
2

Set the root URL

In Admin → Settings → General, set the root URL to your canonical *.cpln.app endpoint, or to your custom domain once you attach one, so that links and tracking URLs inside your emails point at the right host.
3

Choose a media store (optional)

Filesystem storage on the bundled uploads volume set works out of the box. To store media in S3-compatible object storage instead, switch the provider in Admin → Settings → Media.

Backing Up

Database backups are optional and disabled by default. When enabled, a scheduled backup job runs inside the backing PostgreSQL store and uploads to your bucket under the configured prefix — covering lists, subscribers, campaigns, and settings, but not the uploaded media on the listmonk volume set. Enable with postgres.backup.enabled or postgresHA.backup.enabled (matching your database mode), and complete the storage setup for your provider before installing.
1

Create a bucket

Create an S3 bucket. Set backup.aws.bucket and backup.aws.region to match.
2

Set up a Cloud Account

If you do not have one, create a Cloud Account for your AWS account. Set backup.aws.cloudAccountName to its name.
3

Create a bucket-scoped IAM policy

Create an IAM policy granting the required S3 actions on the bucket, and set backup.aws.policyName to its name:
In HA mode, postgresHA.backup.mode selects logical (scheduled pg_dump) or wal-g (continuous WAL archiving). The full per-provider walkthrough lives in the backing postgres and postgres-highly-available template documentation.

Important Notes

  • Single instance by design — there is no replicas knob. Listmonk runs its campaign workers in-process, so two instances against one database would send every campaign twice. The workload is pinned to one replica and rolls out without surge: the old replica stops before the new one starts, which means an upgrade or restart causes a brief gap in availability instead of overlapping senders. Durability comes from PostgreSQL and the uploads volume set.
  • Change admin.password and the database password before installing. Both are applied on the first boot only; editing them afterwards does not change the existing account or database.
  • No mail is delivered until SMTP is configured in Admin → Settings → SMTP. Before that, starting a campaign is not an error — it runs and finishes with zero messages sent.
  • Keep publicAccess enabled for subscriber-facing pages to work. Subscription forms, unsubscribe links, and tracking pixels must be reachable from the internet; the admin UI and API stay authentication-gated either way.
  • Use /health for external health checks, not /api/health — the latter requires an authenticated session.
  • Volumes survive reinstalls under the same release name; uninstalling deletes them — the database volumes and the uploads volume set go with the release, taking all lists, subscribers, campaigns, and media with them. Use postgresHA and/or enable backups for durable production data.

External References

Listmonk Documentation

Official listmonk documentation

Configuration Reference

Settings, SMTP options, and filesystem or S3 media storage

Concepts

How lists, subscribers, campaigns, and templates fit together

API Reference

Manage lists, subscribers, and campaigns over HTTP

Listmonk Template

View the source files, default values, and chart definition