Setting up a free Mailchimp-like newsletter service

Feb 23, 2023  ·  14 min read

⏱ No need for intros? Jump straight to the tutorial.

We’re building a newsletter at PromptHero. When someone says newsletter, my mind immediately goes to Mailchimp, because it’s what I’ve been using forever. Granted, I’ve never ran a big newsletter, mines have always had amounts of subscribers in the hundreds or low thousands, and I’ve never paid a dime for email marketing software.

Is Mailchimp free? Yes, up to a point. Nowadays the Mailchimp free limit is only 500 contacts, but it used to be free up to 5k subscribers, which was more than enough for my needs until now.

Fortunately my project is growing, and these limits no longer seem high enough. In fact, we would like to ideally support dozens of thousands of subscribers, and even more eventually. But this is what happens the moment you exceed 100k emails in Mailchimp:

At $1,075/month, Mailchimp pricing feels a bit too steep

All plans become greyed out with the message “Contact limit exceeded”, leaving you with their most expensive plan as only option, at over $1k a month. I don’t know about you, but paying a monthly amount of money comparable to rent seems like a bit much to me for a newsletter tool.

Why is Mailchimp so expensive?

Well, turns out email marketing is a really difficult problem. And Mailchimp does a damn good job at solving it. Think about it. Here are just the first few problems you’d face if you wanted to build a service like Mailchimp from scratch:

  • Offer a simple way for users to unsubscribe from your newsletter: people become really angry (understandably so) if you send emails without an “Unsubscribe” button. They report your emails as spam, email clients like Gmail pick up the complaints and you might become absolutely invisible to everyone. You need a website where users can self-manage their subscription status, with a unique and private URL per user.
  • Handle bounced emails: sending emails to addresses that bounce your messages hurts your reputation as a sender and can get you banned from sending emails altogether. You need a system in place to detect bounces and remove bad addresses from your mailing list.
  • Email deliverability: you can’t just send emails from your own server, because other email servers don’t trust yours and will label you as spam. You need to send emails from a reputable email server, or your messages will simply not arrive.
  • Technical logistics: your DNS need to be set up right, your DKIM signature has to be valid, same goes for your SPF and DMARC records, your MAIL FROM has to be configured, your email server’s IP can’t be listed in any of the dozens of email blacklists out there, your email server has to have a good reputation, your emails need to have appropriate headers, ideally a List-Unsubscribe header, etc. Here’s a nice checklist of everything that can go wrong and that you should account for.
  • Hosting images and assets: when you attach something in Gmail, like a picture, you’re uploading it to Google’s servers and what gets attached is just a link to the picture, not the picture itself. You need to think about where are you going to upload your email attachments if you’re building a marketing email platform from the ground up.
  • Managing multiple lists: one single user can be subscribed to more than one mailing list. You might have a list for important platform notifications and another list for marketing purposes, and you want to allow users to belong to them and manage them independently. Some of them might be double opt-in, increasing complexity.
  • Emails don’t support standard HTML or CSS. Emails, like websites, use HTML and CSS code to display their content. But if you try to use in emails the same code you use for websites, it won’t work. I learned this the hard way. Emails only support a tiny, specific subset of HTML/CSS, and if you use a visual editor it needs to account for it or your messages will be broken.
  • Tracking marketing metrics: open rates and click-through rates are important metrics for a newsletter, and tracking them (implementing tracking pixels and adding links) requires a whole different system on top of everything we’ve already covered.

Is this worth $1k+/mo? Apparently it is, because Mailchimp is a big company with many customers, but I was not willing to pay the price at this moment.

As a programmer, I obviously considered coding my own newsletter system, but the list of problems I would have needed to tackle kept growing as I kept thinking about it. “Not worth it”, I thought, and like any other lazy programmer I looked for people that had already faced this problem before, hoping they already came with a solution and posted it online.

I started searching for cheaper alternatives to Mailchimp, but look, if I’m already going to pay, I might as well pay for the original. If it’s not a cheaper solution, does that mean I need to look for Mailchimp clones that are… free? And so, I started looking for free alternatives to Mailchimp.

Is there a free version of Mailchimp?

Well, if there is then it has to be open software. Newsletters are a big enough problem: everyone with a project needs to send emails to their users. So it’s likely that many people in the community have already been working on an open source email marketing platform to run your own self hosted newsletter.

And sure enough, there were plenty of projects I came across with. I ended up shortlisting 4 of them – here are the best Mailchimp alternatives I found:

  • Sendy: honestly, it looks great. The only reason I did not go for it is because I was looking for free software exclusively and Sendy comes with a $69 price tag (one-time fee per domain). If my current setup ends up not working out, Sendy is what I’ll try next, even though it’s not free.
  • Mautic [5.7k stars]: free and open source. Looked very complete at first glance, but as I kept watching videos of people actually using it, it became really obvious that it was an unnecessarily complex product. Matic feels as if it had been designed by management consultants and enterprise enjoyers rather than marketers.
  • Mailtrain [5.2k stars]: free and open source, but it felt a bit more immature / half-baked than the rest IMHO. It’s still an option, but not one inviting to try out.
  • Listmonk [9.4k stars]: free and open source, and the most professional-looking one out of all. The interface looked clean and modern, and it looked like it had everything I needed. And at almost 2x the amount of stars on GitHub than its competitors, it seemed to be the most popular of all.

I ended up going for Listmonk.

Now, none of these are hosted services like Mailchimp. You can’t just sign up for them on their website and use them. That’s not how they work. You need to download the code and install it in your own web server. And that’s exactly what we’re going to do.

How to install Listmonk on a production server – a complete step-by-step tutorial

Listmonk is an open source mailing list platform, that is: free software for newsletter publishing. It’s an ideal free alternative to Mailchimp, but it’s self-hosted, meaning you need your own server and need to follow quite a few steps to get it up and running. But once you do, you can run your email marketing for cheap or free, no matter the size of your newsletter. Let’s get to it!

Time needed: 1 hour and 30 minutes.

Here’s a 30,000 foot overview of what we need to do: first, we’ll spin up our own server using AWS – we need our own server to install and run Listmonk on. Then, we’ll install Listmonk on the server. Next we’ll secure everything so it’s ready for production. And lastly, we’ll configure Listmonk to use the AWS platform to send emails and store our message attachments.

Heads up: this guide will get quite technical. If you’re not a developer yourself, you’ll probably need help from one to set everything up. You can email this guide to your developer.

  1. Get a server

    We’re going to run Listmonk on a server hosted on AWS Lightsail. AWS Lightsail offers cheap servers (instances) for as low as $3.5/mo. It also offers pre-configured instances (blueprints) that make our life a bit easier by removing some of the heavy lifting work needed to do the initial setup of the server.

    Go ahead and head to AWS Lightsail. Click on “Create instance” and choose a Linux/Unix machine with the Nginx blueprint.

    Nginx is a web server we’ll use to route traffic to the Listmonk app. This blueprint makes the server ready to use and configured for production use: “Nginx packaged by Bitnami provides a complete production environment. (…) This image is packaged by Bitnami as secure and up-to-date using industry best practices.

    Sounds good! Pick a plan (the $3.5/mo should be enough) and hit “Create instance”.

    Make sure to review your instance details so it runs in the region you want and it uses the correct SSH key pair.

  2. Attach a public static IP address to your instance

    By default, your AWS Lightsail instance comes with a dynamic IP address. This means every time your server gets rebooted it will pick up a new IP address. This is not ideal, as you’ll always need to type a different IP address if you want to connect to your instance.

    Click on your new instance and you’ll get to the instance management page. Navigate to the “Networking” tab and click on “Attach static IP” under its Public IP address box. Follow the steps to attach the static IP. At the end, you’ll see how your instance’s IP address has been fixed to the new static IP address. This IP will now remain unchanged no matter how many times you reboot your server.

  3. Connect to your instance

    Let’s ssh into your instance so we can start messing around.

    This Nginx blueprint from Bitnami comes with an ssh user named, well, bitnami.

    ssh bitnami@your-static-public-ip

    Make sure to replace your-static-public-ip with the IP you’ve just created and make sure you’re using the same SSH key pair you used while launching the instance – and you should be in!

  4. Install Docker

    We need Docker to install and run Listmonk.

    Now, we’re running Bitnami, a Debian-based image. Which means we don’t follow the usual Ubuntu-based steps to install Docker as listed on their official website, but rather we follow this guide on how to install Docker on Bitnami.

    Go ahead and follow it. It should be straightforward, just command after command, and at the end of it you’ll have a running Docker install.

  5. Install docker-compose

    We need to install yet another dependency needed to build and run Listmonk: docker-compose. Run:

    sudo apt-get install -y docker-compose
    sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

  6. Install Listmonk using Docker

    We need to follow the two commands outlined in Listmonk’s official repository to install the production version of Listmonk:

    mkdir listmonk && cd listmonk
    sh -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)"

    Follow the steps and Listmonk should be installed:

  7. Change Listmonk’s default admin user and password

    In your listmonk directory, do ls. You should see two files: config.toml and docker-compose.yml. We need to edit the first one to change the default auth credentials:

    nano config.toml

    And edit the lines where it says admin_username and admin_password. Make sure to add a secure and randomly genereated username and password. Save and exit.

    Now, to pick up the new config, restart the listmonk Docker container (you can see a list of running containers and their names using docker ps):

    docker restart listmonk_app

  8. Configure your DNS to link your domain to your instance so we can connect to Listmonk

    Our instance is now represented by just an IP. Sure, we could connect to it using the IP alone, but since we’re configuring this for production, we’ll be adding an SSL certificate – and we need a domain name or subdomain for that.

    Head to your DNS server settings and point a subdomain (something like mail.example.com) to your instance’s IP address using an A record.

    The exact steps will vary depending on what service you’re using for DNS (Cloudflare, Namecheap, Name.com, etc.), but it will look something like this. Save and exit.

  9. Add a SSL certificate using Bitnami’s SSL script

    When we visit mail.example.com in our browser, nothing will happen (or we’ll just see a default page).

    This is because Listmonk runs on port 9000, while HTTP/HTTPS traffic go to port 80/443, respectively. We need Nginx to route HTTPS traffic from mail.example.com to our Listmonk app running on port 9000. But for that to work, we first need an SSL certificate.

    Bitnami doesn’t use the default LetsEncrypt tool as expected. They have their own script to provide your sites with SSL certificates, the Bitnami HTTPS Configuration Tool, which is fairly simple to use. Just run:

    sudo /opt/bitnami/bncert-tool

    And follow the instructions to generate an SSL certificate for your subdomain.

  10. Configure Nginx to route traffic to Listmonk and use the SSL certificates

    The SSL certificates we’ve just created have been saved to /opt/bitnami/letsencrypt/certificates

    Two certificates have been created in that directory:
    mail.example.com.crt – our public server certificate
    mail.example.com.key – our private key

    We now need to create an Nginx configuration for mail.example.com that will do a few things for us:
    – Redirects all non-secure HTTP traffic on port 80 to HTTPS on port 443
    – Uses the SSL certificates we’ve just created
    – Proxies all traffic to our Listmonk app running on port 9000 via Docker

    I’ve created a ready-to-use Nginx configuration that does all this. You can copy the contents of that link, just make sure to replace mail.example.com everywhere with your actual domain name. Then, use the following command to create your config file and open it in a text editor:

    nano /opt/bitnami/nginx/conf/server_blocks/mail.example.com.conf

    Again, make sure to replace mail.example.com.conf with your actual domain name in the filename. Then paste the edited Nginx config, save the file and you’re ready to go.

    You can run sudo nginx -t to verify the Nginx config is OK.

    To finish setting up Nginx, restart the Nginx server using Bitnami’s tool:

    sudo /opt/bitnami/ctlscript.sh restart nginx

    Now if you navigate to mail.example.com in your browser, you should see your Listmonk login page.

    Congrats! You should be able to log in with the credentials you specified earlier in the config.toml file.

  11. Configure Listmonk basics

    Log in to Listmonk, navigate to the Settings panel and set up the newsletter general info.

    In particular, make sure to set up the Root URL to https://mail.example.com or your links and all email tracking will be broken (make sure to include the https://)

    Since you’re at it, also set up the public appearance of your newsletter by providing a newsletter name and logo your users will see on public pages like signup forms, archive and unsubscribe page.

  12. Configure Listmonk to send email through AWS SES

    Now head to the “SMTP” tab in Settings to configure AWS SES.

    Amazon Simple Email Service (SES) is an AWS service that allows you to send email using AWS’s email servers. This is important because email servers need to have a good reputation to be able to actually deliver emails, and using your own server as your email server will probably not work.

    Head to AWS SES, select the region from which you want to send emails and follow the steps to create an identity. A verified identity is a domain, subdomain, or email address you use to send email through Amazon SES. Identity type would be “Domain”, and this would be mail.example.com in our example.

    In “Advanced DKIM settings”, select “Easy DKIM” and follow their steps to set up the right DNS records. It might be a bit cumbersome, but it’s pretty straightforward.

    Once you’re done setting up your domain in AWS SES, head to the AWS SES left panel, where it says “SMTP settings”, and click the button “Create SMTP credentials”.

    Follow the steps and you’ll get your SMTP Security Credentials. Don’t close this page, as you’ll need to copy these credentials in a second.

    Open your Listmonk’s Settings tab on a different window and navigate to the SMTP tab.

    Click on “Amazon SES” as indicated below, so the appropriate fields get pre-filled.

    Modify the “Host” with your AWS SES region (mine is us-east-1, you can see it in your AWS url, like: https://us-east-1.console.aws.amazon.com)

    And paste the username and password from the AWS SES Credentials page.

    Click “Test Connection” and everything should work just fine!

  13. Increase your AWS SES sending quota

    If you’re using this in a production setting (that is, you’ll be actually sending hundreds or thousands of emails), you’ll probably want to ask AWS to raise your SES sending quota.

    By default, you can only send 200 emails per day on SES. This can be raised for free after AWS examines your case and authorizes you to send more emails (they do this to prevent spam).

    You need to follow these steps to use their Service Quotas Console to open a case and explain your request. They’ll demand to know a few details about the content and nature of your emails and how you’re collecting addresses and users’ consent to be messaged.

  14. [Optional] Configure Listmonk to store attachments in AWS S3

    Attatchments can be stored in the same server you’re running Listmonk on, but we can do better. Let’s store them on AWS S3 for better availability, replication and to avoid overloading our Listmonk server.

    Amazon Simple Storage Service (S3) is an AWS service that allows you to store files. It’s kind of like Dropbox: you have “folders” (buckets), you upload files to them and they are stored in the AWS cloud so you can access them from anywhere. Some of these buckets can be public, so anyone in the world can access the files you upload (ideal for storing things like images in your newsletter attachments).

    Head to AWS S3 and create a new bucket. Set “ACLs enabled” with “Bucket owner preferred” and disable the “Block all public access” checkbox.

    Now, we need a different AWS user for the Listmonk app to access our S3 bucket. We’ll only grant this new user programmatic access via an access key, and we’ll create a strict policy so it only can access the S3 bucket we’ve just created.

    Head to AWS IAM, and create a new user. Name it something like listmonk-s3. Do not “Provide user access to the AWS Management Console”. In permissions, click “Attach policies directly” and click on the “Create policy” button.

    Instead of using the Visual Editor, you can just copy and paste the JSON policy I’ve just created for this purpose. Make sure to edit it and replace listmonk-bucket to the exact name you’ve given to your S3 bucket.

    Create the policy, attach it to your new user, finish creating the user and open their user page in IAM. Navigate to their “Security credentials” tab, scroll down a bit to where it says “Access keys” and create a new access key for the user. Select “Application running outside AWS” and hit next until you can create and get your access key.

    Once you have your Access key and Secret access key, don’t close the window – open the Listmonk settings in a new window and navigate to the “Media upload” tab. Fill up the information as shown below, make sure to edit your own region, and your own bucket name.

  15. Send a test email campaign

    Make sure everything is working by creating a test campaign with an image and sending it to yourself. If you’ve followed the steps correctly, a new test email from your newsletter should have arrived in your inbox!

And that’s it!

You are now able to use Listmonk as a Mailchimp replacement to manage your email marketing campaigns and send emails to thousands of addresses for pennies.

The total cost of this setup is $3.5/mo for the server and $1 for every 10k emails sent, which means you can run the whole thing for less than a cup of coffee a month if you don’t send many emails. This makes Listmonk on AWS a great Mailchimp alternative for nonprofits, for example – or any other kind of project running on a tight budget.

P.S.: Follow me on Twitter to stay in the loop. I'm writing a book called Bold Hackers on making successful digital products as an indie hacker. Read other stories I've written. Subscribe below to get an alert when I publish a new post:

No spam ever, unsubscribe with one single click.

  1. Max
    Mar 22, 2023

    Mailwizz > Sendy.

    Reply
  2. Mark
    Apr 24, 2023

    Thank you for this great tutorial!!

    I actually needed the NGINX stuff to make my listmonk run on a sub domain, but all is written nicely and helpful.

    Reply
  3. beefhooker
    Sep 28, 2023

    Hey rameerez! Thanks for the write-up. I’m interested in doing the same thing you are. How is your set-up doing seven months later? Any hiccups or issues you’ve run into since then, or has everything been going smoothly?

    Reply
    • rameerez
      Sep 29, 2023

      Honestly, apart from a couple of issues we haven’t been using it much!

      I remember having some problems with hitting the AWS SES quota, and having BIG problems sending the mail in a escalonated way. Like, we don’t wanna send 100k emails at once, we might want to try first with 100 or 1000 emails and see how it goes, if it gets delivered, if people like it, etc. before rolling it out to the whole DB. And for that we needed to make custom mailing lists of all users minus the 1000 experiment emails or something. It’s just unconvenient and I’m not sure Mailchimp or any other product solves it. Maybe just us being too cautious?

      Also we got delivery problems in our second issue I think. Like, the first issue would get normal open rates and then the second one would drop to almost zero. Maybe Gmail tagging our mails as spam or something because we’re coming from AWS? I ran email deliverability tests and everything seemed fine but this remained a mystery.

      Keeping your DB in sync with the newsletter is also something you need to do via their API (adding new subscribers, updating email consent, etc.)

      TL;DR: is this Listmok thing a good Mailchimp alternative? Yes, I think. But only if you know what you’re doing, if you really wanna run a big newsletter and if you know and have time to solve the problems that will come along the way. For any other use case I’d choose a hosted service like Mailchimp. Especially if your newsletter is small-ish

      Reply
  4. Jorge
    Mar 13, 2024

    Amazing post.
    Is it possible that you have a tutorial to handle listmonk bounces and be able to blocklist them?
    Also, do you know a tutorial on how to access the tutorial to track who accessed the email or clicked on the links?
    Again, thanks a lot for this amazing post with so clear instructions.

    Reply