Running Listmonk
Introduction
Listmonk is a self-hosted newsletter and mailing list manager. It is a standalone, self-contained application that you can deploy on your own server. It offers a modern web interface for managing subscribers, creating campaigns, and tracking metrics. Listmonk is designed to be fast, efficient, and feature-rich, making it an excellent open-source alternative to commercial email marketing platforms.
listmonk is a standalone, self-hosted, newsletter and mailing list manager. It is fast, feature-rich and packed into a single binary. It uses a PostgreSQL (⩾ v12) database as its data store.
– Listmonk Documentation
Prerequisites
To run Listmonk, you will need:
- Access to a mittwald mStudio project
- A hosting plan that supports containerized workloads
- A PostgreSQL database (version 12 or higher)
How do I start the container?
We use the listmonk/listmonk image from Docker Hub for the container.
Using Terraform (Recommended)
The most convenient way to provision a production-ready Listmonk instance is using Terraform. The following example shows how you can deploy Listmonk along with a PostgreSQL database as a container stack:
data "mittwald_container_image" "postgres" {
image = "postgres:17"
}
data "mittwald_container_image" "listmonk" {
image = "listmonk/listmonk:latest"
}
variable "project_id" {
type = string
}
resource "random_password" "postgres_password" {
length = 24
special = true
}
resource "mittwald_container_stack" "listmonk" {
project_id = var.project_id
default_stack = true
containers = {
database = {
description = "PostgreSQL database for Listmonk"
image = data.mittwald_container_image.postgres.image
entrypoint = data.mittwald_container_image.postgres.entrypoint
command = data.mittwald_container_image.postgres.command
environment = {
POSTGRES_DB = "listmonk"
POSTGRES_USER = "listmonk"
POSTGRES_PASSWORD = random_password.postgres_password.result
}
volumes = [
{
volume = "database"
mount_path = "/var/lib/postgresql/data"
}
]
ports = [
{
container_port = 5432
protocol = "tcp"
}
]
}
listmonk = {
description = "Listmonk newsletter application"
image = data.mittwald_container_image.listmonk.image
entrypoint = data.mittwald_container_image.listmonk.entrypoint
command = ["sh", "-c", "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
environment = {
LISTMONK_app__address = "0.0.0.0:9000"
LISTMONK_db__host = "database"
LISTMONK_db__port = "5432"
LISTMONK_db__user = "listmonk"
LISTMONK_db__password = random_password.postgres_password.result
LISTMONK_db__database = "listmonk"
LISTMONK_db__ssl_mode = "disable"
}
volumes = [
{
volume = "uploads"
mount_path = "/listmonk/uploads"
}
]
ports = [
{
container_port = 9000
public_port = 9000
protocol = "tcp"
}
]
}
}
volumes = {
database = {}
uploads = {}
}
}
Using the mStudio UI
In mStudio, go to your project and select "Create container". A guided dialog will open to assist you with the container setup.
First, enter a description – this is a free text field used to identify the container. For example, enter "Listmonk" and click "Next".
Next, you'll be asked for the image name. Enter listmonk/listmonk and confirm with "Next".
Entrypoint and Command
Configure Listmonk to start with automatic database initialization and upgrades:
- Entrypoint: No changes required
- Command: Select "Custom" and enter
sh -c "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"
Volumes
To persist uploaded files and media, add a volume:
- Path in container:
/listmonk/uploads
Environment Variables
Configure Listmonk to connect to your PostgreSQL database. Click on "Text input" and enter the following environment variables:
# Application settings
LISTMONK_app__address=0.0.0.0:9000
# Database configuration
LISTMONK_db__host=<DB_HOST>
LISTMONK_db__port=5432
LISTMONK_db__user=<DB_USER>
LISTMONK_db__password=<DB_PASSWORD>
LISTMONK_db__database=<DB_NAME>
LISTMONK_db__ssl_mode=disable
Replace the placeholders (<DB_HOST>, <DB_USER>, <DB_PASSWORD>, <DB_NAME>) with your PostgreSQL database connection details.
Once you've entered all the environment variables, click "Next". In the final dialog, you'll be asked for the port – you can leave this unchanged at 9000. Click "Create container" to create and start the container.
Alternative: Using the mw container run command
You can also use the mw container run command to directly create and start a Listmonk container from the command line. This approach is similar to using the Docker CLI and allows you to specify all container parameters in a single command.
user@local $ mw container run \
--name listmonk \
--description "Listmonk Newsletter Manager" \
--publish 9000:9000 \
--env "LISTMONK_app__address=0.0.0.0:9000" \
--env "LISTMONK_db__host=<DB_HOST>" \
--env "LISTMONK_db__port=5432" \
--env "LISTMONK_db__user=<DB_USER>" \
--env "LISTMONK_db__password=<DB_PASSWORD>" \
--env "LISTMONK_db__database=<DB_NAME>" \
--env "LISTMONK_db__ssl_mode=disable" \
--volume "listmonk-uploads:/listmonk/uploads" \
--create-volumes \
-- \
listmonk/listmonk \
sh -c "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"
Make sure to replace the placeholder values with your actual database configuration details. After creating the container, you'll still need to assign a domain to it.
Alternative: Using the mw stack deploy command
Alternatively, you can use the mw stack deploy command, which is compatible with Docker Compose. This approach allows you to define your container configuration in a YAML file and deploy it with a single command.
First, create a docker-compose.yml file with the following content:
services:
listmonk:
image: listmonk/listmonk
command:
[
"sh",
"-c",
"./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''",
]
ports:
- "9000:9000"
volumes:
- "listmonk-uploads:/listmonk/uploads"
environment:
LISTMONK_app__address: "0.0.0.0:9000"
LISTMONK_db__host: "<DB_HOST>"
LISTMONK_db__port: "5432"
LISTMONK_db__user: "<DB_USER>"
LISTMONK_db__password: "<DB_PASSWORD>"
LISTMONK_db__database: "<DB_NAME>"
LISTMONK_db__ssl_mode: "disable"
volumes:
listmonk-uploads: {}
Make sure to replace the placeholder values with your actual database configuration details.
Then, deploy the container using the mw stack deploy command:
user@local $ mw stack deploy
This command will read the docker-compose.yml file from the current directory and deploy it to your default stack.
Assign a Domain
To make Listmonk accessible from the public internet, you need to connect it to a domain:
- In mStudio, navigate to "Domains" in the left sidebar
- Select the domain you want to use (e.g.,
listmonk.example.com) - Under "Set domain target", select "Container"
- Choose your Listmonk container from the dropdown menu
- The port will be automatically set to
9000 - Click the green "Save" button to apply the changes
After a few moments, you should be able to access Listmonk at your configured domain.
Operation
Database Setup
If you're deploying Listmonk alongside a PostgreSQL database in a container stack, you can use the following example to deploy both containers together:
services:
database:
image: postgres:17
ports:
- "5432:5432"
volumes:
- "listmonk-database:/var/lib/postgresql/data"
environment:
POSTGRES_DB: "listmonk"
POSTGRES_USER: "listmonk"
POSTGRES_PASSWORD: "<SECURE_PASSWORD>"
listmonk:
image: listmonk/listmonk
command:
[
"sh",
"-c",
"./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''",
]
ports:
- "9000:9000"
volumes:
- "listmonk-uploads:/listmonk/uploads"
environment:
LISTMONK_app__address: "0.0.0.0:9000"
LISTMONK_db__host: "database"
LISTMONK_db__port: "5432"
LISTMONK_db__user: "listmonk"
LISTMONK_db__password: "<SECURE_PASSWORD>"
LISTMONK_db__database: "listmonk"
LISTMONK_db__ssl_mode: "disable"
depends_on:
- database
volumes:
listmonk-database: {}
listmonk-uploads: {}
Backups
Your Listmonk data is stored in two places:
- PostgreSQL database: Contains subscribers, campaigns, and settings
- Uploads volume: Contains media files uploaded through the web interface
Both are included in the regular project backups and can be restored if needed. For additional protection, consider setting up automated database backups using PostgreSQL's built-in tools.