Listmonk
Newsletter and mailing list manager written in Go, shipped as a single binary with a web dashboard.
- Replaces
-
Paid email newsletter services, such as Mailchimp.
All open-source alternatives to Mailchimp
- Difficulty
-
Difficulty: Medium
2 services (1 database or broker: postgres), 2 volume mounts, no env values to set, no elevated host access. Heuristic score 3 of 11: medium.
- Stack
Go (language), PostgreSQL (database)
- Deployment
docker_compose
- Requirements
Not stated.
- License
-
AGPL-3.0(OSI-approved open source) - Business model
-
Unknown
The README at the pinned commit does not state a business model, so it stays unknown.
Source: github.com/knadh/listmonk/blob/9351a3e/README.md, read on .
- Repository
GitHub stats
- Stars
- 23,908
- Forks
- 2,711
- Open issues and pull requests
- 59
- Last push
- 2026-10-22
- Latest release
- v7.0.0-beta.1
- Released
- 2026-10-20
From the GitHub API on . These numbers are a snapshot, not live. GitHub detected the license as AGPL-3.0.
Compose file
# All LISTMONK_* env variables also support the LISTMONK_*_FILE pattern for loading secrets from files with Docker secrets and Podman
# eg: LISTMONK_ADMIN_USER -> LISTMONK_ADMIN_USER_FILE=/path/to/file_with_value
x-db-credentials: &db-credentials # Use the default POSTGRES_ credentials if they're available or simply default to "listmonk"
POSTGRES_USER: &db-user listmonk # for database user, password, and database name
POSTGRES_PASSWORD: &db-password listmonk
POSTGRES_DB: &db-name listmonk
services:
# listmonk app
app:
image: listmonk/listmonk:latest
container_name: listmonk_app
restart: unless-stopped
ports:
- "9000:9000" # To change the externally exposed port, change to: $custom_port:9000
networks:
- listmonk
hostname: listmonk.example.com # Recommend using FQDN for hostname
depends_on:
- db
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
# --config (file) param is set to empty so that listmonk only uses the env vars (below) for config.
# --install --idempotent ensures that DB installation happens only once on an empty DB, on the first ever start.
# --upgrade automatically runs any DB migrations when a new image is pulled.
environment: # The same params as in config.toml are passed as env vars here.
LISTMONK_app__address: 0.0.0.0:9000
LISTMONK_db__user: *db-user
LISTMONK_db__password: *db-password
LISTMONK_db__database: *db-name
LISTMONK_db__host: db
LISTMONK_db__port: 5432
LISTMONK_db__ssl_mode: disable
LISTMONK_db__max_open: 25
LISTMONK_db__max_idle: 25
LISTMONK_db__max_lifetime: 300s
TZ: Etc/UTC
LISTMONK_ADMIN_USER: ${LISTMONK_ADMIN_USER:-} # If these (optional) are set during the first `docker compose up`, then the Super Admin user is automatically created.
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_ADMIN_PASSWORD:-} # Otherwise, the user can be setup on the web app after the first visit to http://localhost:9000
volumes:
- ./uploads:/listmonk/uploads:rw # Mount an uploads directory on the host to /listmonk/uploads inside the container.
# To use this, change directory path in Admin -> Settings -> Media to /listmonk/uploads
# Postgres database
db:
image: postgres:17-alpine
container_name: listmonk_db
restart: unless-stopped
ports:
- "127.0.0.1:5432:5432" # Only bind on the local interface. To connect to Postgres externally, change this to 0.0.0.0
networks:
- listmonk
environment:
<<: *db-credentials
healthcheck:
test: ["CMD-SHELL", "pg_isready -U listmonk"]
interval: 10s
timeout: 5s
retries: 6
volumes:
- type: volume
source: listmonk-data
target: /var/lib/postgresql/data
networks:
listmonk:
volumes:
listmonk-data:
From the app's own repository: github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml.
Compose file from the app repository.
Install
- Docker with the Compose plugin (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml)
A host with Docker and the Docker Compose plugin. This entry runs from a compose file.
- Save the compose file as docker-compose.yml (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml)
Use the compose file shown above.
- Start it (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml)
docker compose up -d - Open the app (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml)
Open http://localhost:9000. The compose file publishes host port 9000 for the app service.
Compose file check: config_ok on 2026-10-22.
Gotchas and maintenance
Gotchas
- Some images use the latest tag or no tag. Images without a pinned version: app: listmonk/listmonk:latest. A later pull can get a different version. (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml, checked 2026-10-22)
- The compose file publishes host ports. Host ports: 9000 (app), 127.0.0.1:5432 (db). A port without a host address listens on all host network interfaces. (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml, checked 2026-10-22)
- The compose file mounts host folders. Host paths mounted into containers: ./uploads at /listmonk/uploads (app). (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml, checked 2026-10-22)
Signals
- Open issues and pull requests: 59
- Last push: 2026-10-22 (0 days before this page was built)
- Latest release: v7.0.0-beta.1 on 2026-10-20 (1 days before this page was built)