I Pay $0 for a 10,000 Subscriber Self-Hosted Email Newsletter System

listmonk dashboard

Ghost charged me $174/month to manage my 10,000 subscribers.

ConvertKit used to charge me $100/month to manage the same 10,000 subscribers.

Now, my self-hosted Listmonk costs me less than $11/month to manage 10,000 subscribers.

For a small creator like myself, who does not make any revenue from YouTube, Medium, or his website, saving around $2,000/month is a big deal.

In this blog post, I will go over how I migrated from Ghost to Listmonk, how I hosted Listmonk on my server, and how I started sending my newsletters for free.

Here’s a quick lookahead:


- What is Listmonk?
- Listonk Feature Set
- Docker Installation
- Self-hosting with Nginx Proxy
- SMTP Setup with Gmail
- Limiting access with Firewalls
  

If that looks interesting to you, let’s get started.

Listmonk — Free, Open Source, Private!

listmonk dashboard

Listmonk is a self-hosted emailing newsletter and mailing list manager.

Two of its most important features to me are open-source and private. You own your data. When you self-host Listmonk, it uses a Postgres database out of the box that stores all user data.

Whether you want to manage a mailing list of subscribers or design and send out personalized email campaigns, you can do it all with Listmonk.

Feature Set

Previously, I used both ConvertKit and Ghost as my mailing list manager. They were both expensive, but at the same time, their feature set was vast.

When I migrated to Listmonk, to my surprise, I found its features comparable to its more expensive counterparts.

Some notable highlights:

    Store custom JSON attribute per subscriber

    Both single and double opt-ins

    Design personalized templates

    Out-of-the-box API support for mailing list management as well as email campaigns

    Supports multiple “message” types — email, SMS, Whatsapp, etc

    User-friendly analytics dashboard

    Public customizable “subscribe” page

    Easy way to unsubscribe or wipe user data

    Email views and clicks tracking

Listmonk email templates builder
For the majority of small creators, these features are more than enough to get started.

In my opinion, these will take you a long way — maybe even up to 100K subscribers — before you need a more complex (or expensive) solution.

If the feature set and price sound good to you, let me walk you through how I self-hosted Listmonk.

Self-Hosting in a Digital Ocean Server for $11/month

If you visit https://newsletter.irtizahafiz.com, you will find my Listmonk instance.

I host my Listmonk instance on my Digital Ocean server, which is also home to my personal website and web analytics dashboard.

All of those (and more) cost me around $11/month.

Docker Installation

I found that the easiest way to get started with a production-ready version of Listmonk is through Docker.

The installation docs does a great job walking you through the steps. With one bash script, you will get the following:

    Downloads necessary Listmonk Docker images and sets up containers

    Runs a Postgres database container and sets up required schemas

    Runs Listmonk admin UI, giving you full control locally

Nginx Setup and Reverse Proxy

Once the docker container is up and running, you can access your Listmonk dashboard through your server’s port 9000.

So, if you go to http://localhost:9000, you can access your Listmonk dashboard.

However, I wanted to expose my instance to the public internet (of course behind a strong password). To do that, I used Nginx reverse proxy.


server {
    listen 443 ssl;
    server_name newsletter.irtizahafiz.com;
    location / {
        proxy_pass http://127.0.0.1:9000;
    }
    location = /api/public/subscription {
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'POST';
            add_header 'Access-Control-Allow-Headers' 'Content-Type';
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }

        if ($request_method = 'POST') {
            add_header 'Access-Control-Allow-Origin' '*';
        }

        proxy_pass http://127.0.0.1:9000;
    }
    ssl_certificate /etc/letsencrypt/live/irtizahafiz.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/irtizahafiz.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

}

server {
    listen 80;
    server_name newsletter.irtizahafiz.com; # Redirect HTTP requests to HTTPS
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }
}

The above Nginx configuration does the following:

    Routes (or proxies) any HTTP or HTTPS request from newsletter.irtizahafiz.com to the server’s port 9000 (where Listmonk is running)

    Adds the relevant CORS headers to allow cross-origin requests so that my website’s client can make API requests to manage Listmonk mailing lists and newsletters

    Allows POST requests to create new subscribers (still requires AUTH headers)

With the above setup, I can do both:

    Access the Listmonk dashboard from the public internet to create email campaigns, manage mailing lists, and check analytics

    Access Listmonk’s API to programmatically interact with subscriber and campaign data

SMTP Setup (with Gmail)

Listmonk SMTP Configuration

When it comes to sending emails out, Listmonk requires an SMTP client.

I could have used my personal domain’s SMTP, but it’s likely emails coming from my domain might be categorized as spam.

Instead, I chose a more reputable SMTP client — Gmail.

Through the Listmonk dashboard settings, it took me 2 minutes to link my business Gmail account. Now, all email campaigns Listmonk sends use my business Google email, and it’s FREE.

In the future, I plan on migrating from Gmail to Protonmail, but currently, Protonmail only supports SMTP in its business plan, which I don’t use.

(Coming Up) Limit API Access using a Firewall

Given that Listmonk’s dashboard is very powerful, it’s sufficient for all my needs.

Because of that, the only use I make of its API is to add new subscribers. To be more specific, for https://irtizahafiz.com/newsletter to make POST requests to add new subscribers to my Listmonk’s Postgres database.

So, in the next couple of days, I plan on limiting API access to that route only. It only takes a few lines of Nginx code, but I am lazy.

Closing Thoughts

If you have made it this far, I hope you found this valuable.

Feel free to follow me on Medium, subscribe to my website, or follow me on YouTube.

Irtiza Hafiz

;