What is Listmonk and who is it for ?
Listmonk is a small-ish project that provides a tool quick & easy to setup and manage for those who want to do mailing on a large scale and need go get it done today:
-
the container is ready in 2 seconds when deploying from scratch
-
the UI is as simple and direct as it gets
-
it comes with ready to use templates out of the box
In this article, we will cover an alternative to the setup suggested by the official documentation and have a look at the different functionnalities available.
-
Official website: Listmonk
-
Documentation: Listmonk documentation
-
Docker images: Listmonk images
-
Git repository: Listmon's Github repository
Deploying a container-based Listmonk instance
Why not follow the official documentation ?
On both the official website and the Listmonk image repository, the instructions are to use a ready-made docker-compose.yml file in conjunction with an accompanying shell script depending on wether you want to deploy a demo or a production site.
This docker-compose.yml deploys both the Listmonk app and a PostgreSQL database: it's actually one off the selling arguments that Listmonk is "self-hosted".
I didn't want to have an extra container for DB hosting considering I've already set up a dedicated DB server and Listmonk will actually be off most of the time as I only need to send a newsletter once a month. So I really wanted to minimise the ressource usage to a minimu.
When deploying in a more professional environment with a more robust infrastructure, I would recommend following the official instructions
Pre-requisite for this deployment method
-
A PostgreSQL dedicated database and user
CREATE USER listmonkadmin with password 'Averypassword4Listmonk!'; CREATE DATABASE listmonk with owner listmonkadmin ; GRANT ALL PRIVILEGES ON DATABASE listmonk TO listmonkadmin;
-
Ubuntu 22.04 server up to date with docker-compose installed
sudo apt update -y sudo apt upgrade -y sudo apt install -y docker-compose
-
Ubuntu 22.04 server must be able to connect to the dedicated database:
psql -h <DBHOSTIP> -U keycloakadmin -p 5432 dbserver
-
Ubuntu 22.04 server must be able to pull docker images from Listmonk Docker image registry
-
Set up a dedicated sub-domain.
For our example listmonk.nicksopenworld.com -
Optional: a Traefik service to manage HTTPS certificates
Keycloak can manage HTTPS if provided with certificate files but having Traefik or an equivalent makes it so much easier. -
the listmonk setup shell script, available on my fork of the project
Files and folders configuration & setup
For this demonstration, a sub-directory "listmonk" has been created in /opt containing:
File | docker-compose.yml | File at the root of the directory containing our running image configuration (see below) |
---|---|---|
File | config.toml | File containing the basic Listmonk parameters |
File | listmonksetup.sh | File containing the basic Listmonk parameters |
docker-compose.yml
version: "3.9"
services:
listmonk:
image: listmonk/listmonk:v2.5.1
container_name: listmonk
command: [sh, -c, "yes | ./listmonk --install"]
restart: unless-stopped
ports:
- "9050:9050"
environment:
- TZ=Etc/UTC
- LISTMONK_db__database=listmonkdb
- LISTMONK_db__user=listmonkdbadmin
- LISTMONK_db__password='Pas$word4ListmonkDB'
volumes:
- /opt/listmonk/config.toml:/listmonk/config.toml
- /opt/listmonk/uploads:/listmonk/uploads:rw
config.toml
As can be seen in the docker-compose.yml file, this file is on the container host.
It defines some of the parameters for Listmonk such as Admin UI credentials and database connection.
[app]
# Interface and port where the app will run its webserver. The default value
# of localhost will only listen to connections from the current machine. To
# listen on all interfaces use '0.0.0.0'. To listen on the default web address
# port, use port 80 (this will require running with elevated permissions).
address = "0.0.0.0:9050"
# BasicAuth authentication for the admin dashboard. This will eventually
# be replaced with a better multi-user, role-based authentication system.
# IMPORTANT: Leave both values empty to disable authentication on admin
# only where an external authentication is already setup.
admin_username = "papaemeritus"
admin_password = "IfUhaveg0hst$"
# Database.
[db]
host = "192.168.22.22"
port = 5432
user = "listmonkdbadmin"
password = "Pas$word4ListmonkDB"
database = "listmonkdb"
ssl_mode = "disable"
max_open = 25
max_idle = 25
max_lifetime = "300s"
A few values must be matched between docker-compose.yml and config.toml:
docker-compose.yml | config.toml |
---|---|
ports | port |
database | |
user | |
password |
listmonksetup.sh
As explained, the standard deployment didn't match my usage.
This script will:
- deploy a Listmonk Docker container with the "--install" command
- wait 2 seconds for the DB configuration finish
- check for specific strings in the Listmonk Docker container logs
- if the logs are as expected, the running container will be forcefully deleted
- a backup copye of the docker-compose.yml file will be done
- the command instruction will be commented out of the docker-compose.yml file
- a new Listmonk Docker container will be relaunched
The script can be found on my fork of the Git repository.
DOCKERCOMPOSEDIRECTORY='/opt/listmonk'
sudo docker-compose up -d
sleep 2
# counts occurences of specific strings in log
IntallLaunched=$(sudo docker logs listmonk | grep 'config' | wc -l)
IntallFinished=$(sudo docker logs listmonk | grep 'run the program and access the dashboard' | wc -l)
CleanUp=0
echo IntallLaunched: $IntallLaunched
echo IntallFinished: $IntallFinished
while [ $CleanUp -eq 0 ]; do
IntallLaunchedretest=$(sudo docker logs listmonk | grep 'config' | wc -l)
IntallFinishedretest=$(sudo docker logs listmonk | grep 'run the program and access the dashboard' | wc -l)
sleep 2
echo IntallLaunched: $IntallLaunched
echo IntallFinished: $IntallFinished
if [ "$IntallLaunchedretest" -gt 1 ] || [ "$IntallFinishedretest" -gt 1 ]; then
sudo rm -f listmonk
# update docker-compose.yml to not force install
sudo cp docker-compose.yml /home/ubuntu/
echo "Backup copy of docker compose file to /home/ubuntu/"
sudo cp docker-compose.yml /tmp/dc.yml
# Comment out install command
sudo sed -i 's/command:/#command:/g' /tmp/dc.yml
sudo mv /tmp/dc.yml $DOCKERCOMPOSEDIRECTORY/docker-compose.yml
echo "Docker compose file updated"
# Relaunch Listmonk
sudo docker-compose up -d
# Increment cleanup value to get out of loop
CleanUp=$((CleanUp+1))
fi
done
Actual deployment
Simply execute the listmonksetup.sh script:
bash listmonksetup.sh
A working & funcional Listmonk instance will be made available after a few seconds:
If the command line is not commented out in the docker-compose.yml, the DB setup will loop as can be seen in the Listmonk container logs:
The error message:
first time installation
IMPORTANT: This will wipe existing listmonk tables and types in the DB 'listmonk'
continue (y/N)? 2023/11/23 21:47:39 install.go:179: setup complete
2023/11/23 21:47:39 install.go:180: run the program and access the dashboard at 0.0.0.0:9050
2023/11/23 21:47:41 main.go:102: v2.5.1 (a6a2b69 2023-08-15T15:49:28Z, linux/amd64)
2023/11/23 21:47:41 init.go:145: reading config: config.toml
2023/11/23 21:47:41 init.go:273: connecting to db: 172.10.11.201:5442/listmonk
First connection to your Listmonk instance
Go to the URL for your Listmonk instance and log-in using the credentials set in the config.toml file:
Unfortunately, there can only be 1 access account.
There are ideas to improve on this situation as can be seen in open issues such as Multi User support #543 or even in the config.toml.sample
provided in the Github repo:
# BasicAuth authentication for the admin dashboard. This will eventually # be replaced with a better multi-user, role-based authentication system. # IMPORTANT: Leave both values empty to disable authentication on admin # only where an external authentication is already setup.
On a more positive note, the login page can be customized as we will see below.
Essential configuration
General settings
In this tab, you must set the root URL for your instance: although it is not needed to access the platform once deployed, it will be used to build URLs for login pages or to redirect users for opt-in/out actions.
Leaving the values to the default "localhost:9000" will only create errors later.
Similarly, this is where you set the default "FROM" email and set the recipient(s) for Admin-level notifications such as the end of a campaign:
You can also set your own images for favicons and logo.
Media management
Using local storage
It requires your docker-compose.yml file to have in the volumes declarations:
- /opt/listmonk/uploads:/listmonk/uploads:rw
It means that any media (image, attachment, ...) uploaded to Listmonk will be saved on the Docker container's host in the folder you have set ("/opt/listmonk/uploads" in our example).
Using Amazon S3
If you use Amazon AWS services and you want to separate media storage from the container host or you are uploading a very large number of media files requiring a larger storage space, S3 can be an interresting alternative.
The files uploaded through Listmonk can then be accessed through the AWS S3 portal or CLI:
File filtering
A filter is available to set a white liste of file extensions to allow.
Note that a filter * is set allowing all formats.
SMTP
This is a standard configuration pannel but critical to Listmonk as it allows emails to be sent from multiple sources:
- Amazon SES
- Gmail
- Mailgun
- Mailjet
- Postmark
- Sendgrid
- Standard SMTP server
Selecting one of the option will pre-fill some of the fields and change available options but the configuration is basically always the same.
Note: you can add multiple SMTP configurations but only 1 can be used at a time.
This essentially allows you to quickly switch between SMTP providers if you need to.
Switching between providers can be done due to the "FROM" email you want to use, to be updated in the general settings, or simply due to the volume of emails to be sent in which case you might also want to check the Performance tab:
This menu allows you to manage concurrent sending threads and batch sizes to avoid application overload.
Messenger
If you want to push your message through a messaging system or using SMS instead of emails, you can configure the endpoing connexions that can then be selected when configuring a campaign.
Only 1 messenger can be used per campaign so if you want to push the same message through multiple communication channels, you will have to clone the campain.
Logs & maintenance
Useful in the initial setup, logs are accessible in the dedicated menu and you can
You will be able to find information regarding configuration errors but also campaign status.
And of course, if after doing some debugging you want to clean up your platform, go to Maintenance:
Conclusion
With this short article you should now have a working platform that can send emails or messages with nice logos and/or attachments.
Email lists, campaigns and user management will be detailed separately in articles published in the Listmonk category.