OpenLogs

All tools

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)

Read from github.com/knadh/listmonk/blob/9351a3e/LICENSE

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

knadh/listmonk

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

Config check passed docker compose config exited 0 on this exact file on 2026-10-22.
docker-compose.yml Version 1
# 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

  1. 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.

  2. Save the compose file as docker-compose.yml (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml)

    Use the compose file shown above.

  3. Start it (github.com/knadh/listmonk/blob/9351a3e/docker-compose.yml)
    docker compose up -d
  4. 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

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)