Skip to content

Listmonk

Self-hosted newsletter and mailing list manager. Listmonk handles subscriber management, list segmentation, campaign creation, email template rendering, and delivery tracking. It requires PostgreSQL and an external SMTP provider to send emails.

SMTP must be configured before sending any emails

Listmonk does not ship with an SMTP server. After the first login, navigate to Settings → SMTP and configure your email provider (Postmark, AWS SES, Mailgun, or any SMTP relay). Without SMTP configuration, the application is fully functional but cannot send any campaigns or transactional emails.

Key Features

  • Subscriber and list management — import, segment, and manage subscriber lists
  • Campaign management — HTML and plain-text email campaigns with template support
  • Transactional emails — API-driven transactional message delivery
  • PostgreSQL backend — bundled subchart or external database with pgcrypto auto-provisioned
  • Persistent uploads storage — media and attachment files on a dedicated PVC
  • Idempotent bootstrap — init container applies schema migrations on every pod start
  • S3 backup — scheduled PostgreSQL pg_dump to S3-compatible storage

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install listmonk helmforge/listmonk

OCI registry:

helm install listmonk oci://ghcr.io/helmforgedev/helm/listmonk

After the first pod starts, access the Listmonk admin UI via port-forward and complete the setup wizard to set the admin password and configure SMTP:

kubectl port-forward svc/<release>-listmonk 9000:80
# Open http://localhost:9000

Deployment Examples

# values.yaml — Listmonk with bundled PostgreSQL
# After deploying, configure SMTP in the admin UI: Settings → SMTP
postgresql:
  enabled: true
  auth:
    password: 'postgres-password'

storage:
  enabled: true
  size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: listmonk.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Production Listmonk with TLS and daily PostgreSQL backup
# Backup covers the PostgreSQL database only. Uploads PVC is not included.
postgresql:
  enabled: true
  auth:
    password: 'postgres-password'

storage:
  enabled: true
  size: 10Gi

backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: my-listmonk-backups
    prefix: listmonk
    existingSecret: listmonk-s3-credentials

resources:
  requests:
    memory: 128Mi
    cpu: 100m
  limits:
    memory: 512Mi
    cpu: 500m

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: listmonk.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: listmonk-tls
      hosts:
        - listmonk.example.com
# values.yaml — Listmonk with external managed PostgreSQL
# The external PostgreSQL database must have the pgcrypto extension available.
database:
  mode: external
  external:
    host: postgresql.database.svc.cluster.local
    port: 5432
    name: listmonk
    username: listmonk
    existingSecret: listmonk-db-credentials
    existingSecretPasswordKey: database-password
    sslMode: require

postgresql:
  enabled: false

storage:
  enabled: true
  size: 10Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: listmonk.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Configure SMTP via environment variables (bypasses UI)
# Listmonk reads SMTP config from environment when set.
# Refer to Listmonk environment variable documentation for all available keys.
listmonk:
  extraEnv:
    - name: LISTMONK_smtp__host
      value: 'smtp.postmarkapp.com'
    - name: LISTMONK_smtp__port
      value: '587'
    - name: LISTMONK_smtp__auth_protocol
      value: 'login'
    - name: LISTMONK_smtp__username
      valueFrom:
        secretKeyRef:
          name: listmonk-smtp
          key: username
    - name: LISTMONK_smtp__password
      valueFrom:
        secretKeyRef:
          name: listmonk-smtp
          key: password

postgresql:
  enabled: true
  auth:
    password: 'postgres-password'

storage:
  enabled: true
  size: 5Gi

Configuration Reference

Core

ParameterTypeDefaultDescription
nameOverridestring""Override the chart name.
fullnameOverridestring""Override the full release name.
commonLabelsobject{}Extra labels added to all resources.
replicaCountinteger1Number of Listmonk replicas. Keep at 1 to avoid scheduling conflicts.

Image

ParameterTypeDefaultDescription
image.repositorystringdocker.io/listmonk/listmonkListmonk container image.
image.tagstring"v6.1.0"Image tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

Listmonk Configuration

ParameterTypeDefaultDescription
listmonk.extraEnvarray[]Extra environment variables. Use for SMTP, admin credentials, and feature flags.
Configure SMTP via environment variables for GitOps workflows

The Listmonk UI stores SMTP configuration in the PostgreSQL database. For GitOps or automated deployments, set SMTP settings via listmonk.extraEnv using the LISTMONK_smtp__* environment variable naming convention. Environment variables take precedence over database-stored settings.

Database

ParameterTypeDefaultDescription
database.modestringautoDatabase mode: auto, external, or postgresql.
database.external.hoststring""External PostgreSQL hostname.
database.external.portinteger5432External PostgreSQL port.
database.external.namestringlistmonkDatabase name on the external server.
database.external.usernamestringlistmonkUsername for the external database.
database.external.passwordstring""Password for the external database (prefer existingSecret).
database.external.sslModestringdisablePostgreSQL SSL mode for external connections.
database.external.existingSecretstring""Existing secret containing the database password.
database.external.existingSecretPasswordKeystringdatabase-passwordKey inside the existing secret for the password.
database.mode: auto selects the right backend automatically

With database.mode: auto (the default), the chart uses the bundled PostgreSQL subchart when postgresql.enabled: true, and falls back to the external configuration otherwise. Set database.mode explicitly to external or postgresql when you need deterministic behavior in production.

Database — Embedded Subchart

The bundled PostgreSQL subchart automatically provisions the pgcrypto extension and the required grants via init SQL scripts. This extension is mandatory for Listmonk — deployments against external databases must ensure pgcrypto is available.

ParameterTypeDefaultDescription
postgresql.enabledbooleantrueDeploy the bundled PostgreSQL subchart.
postgresql.architecturestringstandalonePostgreSQL architecture.
postgresql.auth.databasestringlistmonkDatabase name created by the subchart.
postgresql.auth.usernamestringlistmonkDatabase user created by the subchart.
postgresql.auth.passwordstring""Database password. Auto-generated if empty.
postgresql.auth.postgresPasswordstring""PostgreSQL superuser password. Auto-generated if empty.
postgresql.standalone.persistence.enabledbooleantrueEnable persistence for PostgreSQL data.
postgresql.standalone.persistence.sizestring8GiPVC size for PostgreSQL data.

Uploads Storage

ParameterTypeDefaultDescription
storage.enabledbooleantrueEnable a dedicated PVC for uploaded media and attachments.
storage.sizestring5GiUploads PVC size.
storage.storageClassstring""StorageClass for the uploads PVC.
storage.accessModestringReadWriteOncePVC access mode.
storage.existingClaimstring""Use an existing PVC for uploads.
storage.annotationsobject{}Annotations for the uploads PVC.
S3 backup covers PostgreSQL only — uploads PVC is not included

The built-in S3 backup CronJob runs pg_dump against the PostgreSQL database. Uploaded media, images, and attachments stored in the uploads PVC (storage.*) are not included in this backup. Implement a separate backup strategy (e.g. Velero volume snapshot or NFS-level backup) for the uploads PVC.

Backup

ParameterTypeDefaultDescription
backup.enabledbooleanfalseEnable scheduled PostgreSQL S3 backup CronJob.
backup.schedulestring"0 3 * * *"Cron schedule for backups.
backup.suspendbooleanfalseSuspend the CronJob without deleting it.
backup.concurrencyPolicystringForbidCronJob concurrency policy.
backup.successfulJobsHistoryLimitinteger3Number of successful Job records to keep.
backup.failedJobsHistoryLimitinteger3Number of failed Job records to keep.
backup.backoffLimitinteger1Job retry limit.
backup.archivePrefixstringlistmonkPrefix for backup archive filenames.
backup.images.postgresqlstringdocker.io/library/postgres:17-alpineImage for pg_dump.
backup.images.uploaderstringdocker.io/helmforge/mc:1.0.0Image for S3 upload.
backup.resourcesobject{}Resources for backup containers.
backup.database.pgDumpArgsstring""Extra arguments passed to pg_dump.
backup.s3.endpointstring""S3-compatible endpoint URL.
backup.s3.bucketstring""Target bucket name.
backup.s3.prefixstringlistmonkKey prefix within the bucket.
backup.s3.createBucketIfNotExistsbooleantrueCreate the bucket automatically if it does not exist.
backup.s3.existingSecretstring""Existing secret containing S3 access and secret keys.
backup.s3.existingSecretAccessKeyKeystringaccess-keyKey in the existing secret for the S3 access key.
backup.s3.existingSecretSecretKeyKeystringsecret-keyKey in the existing secret for the S3 secret key.
backup.s3.accessKeystring""Inline S3 access key (ignored when existingSecret is set).
backup.s3.secretKeystring""Inline S3 secret key (ignored when existingSecret is set).

Service

ParameterTypeDefaultDescription
service.typestringClusterIPKubernetes service type.
service.portinteger80Service port exposed to the cluster.
service.annotationsobject{}Annotations for the Service.

Ingress

ParameterTypeDefaultDescription
ingress.enabledbooleanfalseEnable an Ingress resource.
ingress.ingressClassNamestring""Ingress class name. Must be set explicitly.
ingress.annotationsobject{}Annotations for the Ingress (e.g. cert-manager).
ingress.hostsarray[]Ingress host and path rules.
ingress.tlsarray[]TLS configuration (secret name and hosts).

Probes

ParameterTypeDefaultDescription
startupProbe.enabledbooleantrueEnable startup probe.
startupProbe.initialDelaySecondsinteger10Startup probe initial delay.
startupProbe.periodSecondsinteger5Startup probe period.
startupProbe.timeoutSecondsinteger3Startup probe timeout.
startupProbe.failureThresholdinteger30Startup probe failure threshold.
livenessProbe.enabledbooleantrueEnable liveness probe.
livenessProbe.initialDelaySecondsinteger0Liveness probe initial delay.
livenessProbe.periodSecondsinteger30Liveness probe period.
livenessProbe.timeoutSecondsinteger5Liveness probe timeout.
livenessProbe.failureThresholdinteger3Liveness probe failure threshold.
readinessProbe.enabledbooleantrueEnable readiness probe.
readinessProbe.initialDelaySecondsinteger0Readiness probe initial delay.
readinessProbe.periodSecondsinteger10Readiness probe period.
readinessProbe.timeoutSecondsinteger3Readiness probe timeout.
readinessProbe.failureThresholdinteger3Readiness probe failure threshold.

Resources and Security

ParameterTypeDefaultDescription
resourcesobject{}CPU and memory requests and limits.
podSecurityContextobject{}Pod-level security context.
securityContextobject{}Container-level security context.

Service Account

ParameterTypeDefaultDescription
serviceAccount.createbooleanfalseCreate a dedicated ServiceAccount.
serviceAccount.namestring""Override the ServiceAccount name.
serviceAccount.annotationsobject{}Annotations for the ServiceAccount.

Scheduling

ParameterTypeDefaultDescription
nodeSelectorobject{}Node selector for scheduling.
tolerationsarray[]Tolerations for scheduling.
affinityobject{}Affinity rules.
priorityClassNamestring""PriorityClass for the pod.
terminationGracePeriodSecondsinteger30Termination grace period.
podLabelsobject{}Extra labels for the pod.
podAnnotationsobject{}Extra annotations for the pod.

Extra

ParameterTypeDefaultDescription
extraVolumesarray[]Extra volumes to attach to the pod.
extraVolumeMountsarray[]Extra volume mounts for the container.
extraManifestsarray[]Extra Kubernetes manifests deployed alongside the chart.

Email Deliverability

Configure SPF, DKIM, and DMARC before sending campaigns

Listmonk is responsible for campaign delivery, but email deliverability depends on DNS records configured on the sending domain. Before sending any campaign, ensure your sending domain has valid SPF, DKIM, and DMARC records. Without them, emails from large campaigns are likely to land in spam folders or be rejected entirely by recipients’ mail servers.

More Information