Moving from SubStack to listmonk
I publish an infrequent newsletter of recent posts and stories from some of my travels and adventures. It used to be on SubStack but that place seems to have gone a bit sour for subscribers lately and I’ve fallen out of love with it. I still read stuff there but no longer post. All of my stuff pretty much ends up on sptr.net anyway.
After reviewing a number of options, most of which involve handing my content and subscriber details to a corporation of some kind, I resolved to host an email service on my own server using the free and open source listmonk. That way, it is secure, and when I go, it goes. No eternal presence to be gleefully exploited to make wealthy people wealthier. I am getting quite tired of that. Anyway, here’s what I did.
Export subscriber details from SubStack
This step was easy enough and does not need explaining except to say when you read the csv download, you realise just how much metrics and profiling information becomes attached to a subscriber’s name. This is what you get:
Email,Name,Stripe plan,Cancel date,Start date,Paid upgrade date,Bestseller,Emails received (6mo),Emails dropped (6mo),num_emails_opened,Emails opened (6mo),Emails opened (7d),Emails opened (30d),Last email open,Links clicked,Last clicked at,Unique emails seen (6mo),Unique emails seen (7d),Unique emails seen (30d),Post views,Post views (7d),Post views (30d),Unique posts seen,Unique posts seen (7d),Unique posts seen (30d),Comments,Comments (7d),Comments (30d),Shares,Shares (7d),Shares (30d),Subscriptions gifted,First paid date,Revenue,Subscription source (free),Subscription source (paid),Days active (30d),Activity,Country,State/Province,Expiration date,Type,Sections
example@example.com,Fred Bloggs,,,2024-03-24T21:24:16.280Z,,,0,0,0,0,0,0,,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,,$0.00,substack-notes,,0,0,GB,,,Free,"The NixImagery SubStack,"
Prepare subscriber import file
You don’t need all of that information. Edit the csv down to this:
email,name,attributes
example@example.com,Fred Bloggs,"{""country"":""GB""}"
The attributes are a JSON map containing arbitrary information about a user if you wish to use it. See the documentation. You need to pay attention to the quotes here, the JSON map is a string containing strings.
Install listmonk
I installed this on a Debian Beelink box that runs behind my router at home. The documentation suggests Docker to containerise the installation but I don’t like to load a lightweight server, so I installed the binary and its only real dependency, which is the postgres database software. I don’t need Docker just for this.
# update your system first
sudo apt update && sudo apt upgrade -y
# install the database software
sudo apt install -y postgresql postgresql-contrib wget nano
# configure the database
sudo -u postgres psqlSet up the database and permissions:
CREATE DATABASE listmonk;
CREATE USER listmonk WITH ENCRYPTED PASSWORD 'your_strong_db_password_here';
GRANT ALL PRIVILEGES ON DATABASE listmonk TO listmonk;
\qI found there were permissions errors at this point, which required logging back in to postgres and explicitly connecting to the listmonk database to correct:
sudo -u postgres psql listmonkALTER SCHEMA public OWNER TO listmonk;
GRANT ALL ON SCHEMA public TO listmonk;
GRANT ALL PRIVILEGES ON DATABASE listmonk TO listmonk;sudo systemctl restart postgresqlNow we can download the binary and checksum, check they are OK before installing. I am using 5.1.0 but check for later versions:
# download
wget https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_linux_amd64.tar.gz
wget https://github.com/knadh/listmonk/releases/download/v5.1.0/listmonk_5.1.0_checksums.txt
# check
sha256sum -c listmonk_5.1.0_checksums.txt
# unpack and install (you might want to do this in a separate directory - I didn't)
tar -xvf listmonk_5.1.0_linux_amd64.tar.gz
sudo install -v listmonk /usr/local/bin/Configuration
You need to have listmonk create a new configuration file which you can edit:
listmonk --new-config
nano config.tomlThe config file:
[app]
address = "0.0.0.0:9000"
admin_username = "admin"
admin_password = "your_admin_password"
[db]
host = "localhost"
port = 5432
user = "listmonk"
password = "your_strong_db_password_here"
database = "listmonk"
ssl_mode = "disable"Now you’re ready to initialise the database schema and run the service:
listmonk --install
listmonkReady to play
You can now visit your listmonk web user interface at http://your_server_ip_address:9000, using the admin details you put in the config file.
To make listmonk run as a service and start automatically after a reboot, then create a service file at /etc/systemd/system/listmonk.service as follows:
[Unit]
Description=Listmonk Newsletter Manager
After=network.target
[Service]
ExecStart=/usr/local/bin/listmonk
WorkingDirectory=/home/your_user/
Restart=always
User=your_user
Group=your_user
Environment=LISTMONK_CONFIG=/path/to/config.toml
[Install]
WantedBy=multi-user.target# enable the service
sudo systemctl daemon-reload
sudo systemctl enable listmonk
sudo systemctl start listmonkGoing outdoors
I wanted the interface to be visible on the web, so I had to open up port 9000 in the local router and point list.sptr.net at it in the DNS. Because the server already runs Apache as a web server, I created an additional virtual host to act as a reverse proxy.
sudo a2enmod proxy proxy_http
sudo systemctl restart apache2<VirtualHost *:443>
ServerName list.sptr.net
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:9000/
ProxyPassReverse / http://127.0.0.1:9000/
ErrorLog ${APACHE_LOG_DIR}/listmonk_error.log
CustomLog ${APACHE_LOG_DIR}/listmonk_access.log combined
</VirtualHost>
<VirtualHost *:80>
ServerName list.sptr.net
Redirect permanent / https://list.sptr.net/
</VirtualHost>Don’t forget to run certbot so you can connect securely over ssl. For media/image uploads, you need to make sure your listmonk upload folder is readable and publicly visible. I created a new one:
sudo mkdir /opt/mystuff
sudo mkdir /opt/mystuff/uploads
sudo chown -R youruser:yourgroup /opt/mystuff/uploads/
sudo systemctl restart listmonkUp and running
Obviously, testing is crucial to save yourself from embarrassment with your subscribers, so check and test everything works as you want it to by using a few test lists, campaigns and email accounts. Follow the documentation – the subscriber fields are really useful.
Once that’s done, you have a fully operational newsletter service, free of corporate nonsense and free of charge. Gotta love open source.