How to run a Ghost blog for (virtually) zero cost and why you should

How to run a Ghost blog for (virtually) zero cost and why you should
Photo by Fakurian Design / Unsplash

Now, I will preface this with the fact that you can see the word "virtually" in brackets and that your results may vary, I'm relatively small on Twitter and GitHub, so consequently my requirements from a Ghost blog are relatively limited. But hey, if that's what you're looking for, you've hit the jackpot.

Why Ghost?

Simple reason, you own your data. I mean it's not that simple, theoretically your cloud provider could shut you down, or your CDN might pull their services or ISPs might block you, hell, lightning can strike you down at this very moment, but for the most part and the fact that Zeus has been missing for a while, you can own your data.

At this stage of the internet, where computing power is so cheap, I find it virtually impossible to fathom that sites like Medium and Wordpress are profiting off from free bloggers - the first service that paywalls free content, the latter with absolutely unnecessary and inane ads on their free tier. Now let's be honest, they are businesses and they were always made to make money, but hey we never asked them to.

A good question at this point would be, why should you use Ghost over the hundreds of thousands of CMSes out there, the reason is because most of them suck. Sure, once in a while they do come out with something good but Ghost is the rare CMS that works as intended and it's also open-source, an absolute rare gem in the highly irradiated and hazardous cesspool that freemium platforms can be (and trust me, people love it). But hey, if you have a hard time believing me, the people who made Ghost have also made a lot of comparison charts for you to look at. What's better? They have the names to back it up.

Let's get started

Firstly, let's get a VM, if you have your own server, that's cool too. Ideally if you're starting off, you want a cheap VM that doesn't burn through your annual budget in a day, so free-tier VMs are perfect, AWS, Azure and Google Cloud all have free-for-12-months offerings now, although I highly recommend against Google Cloud solely because the memory capacity on their free machines is terrible. AWS and Azure will work fine, with a slight caveat I'll come to later. If you have slightly more leeway on the budget front, you can even consider Digital Ocean droplets and Linode VPSes, which combined with the free credits they often offer and the capacity they usually provide will work nicely for a couple of months (and maybe even years).

Run the latest

You want your CMS to last and you want to upgrade as less as possible, so whenever you're installing anything, have some forethought and install the latest stable versions, save yourself some headache one year down the line. This means that you should use Ubuntu 22.04 instead of 20.04, Debian bullseye instead of buster, Node 16 over Node 14 and so on... Basic stuff really.

The caveat

Now, the little guide that the people at Ghost offer you, is quite good, but it was never made for small pencil-pushers and zero-budget folks like us, was it? So the official guide recommends a minimum of 1GB of memory if you want to run Ghost and ghost doctor (a tool that tells you what is wrong with your Ghost installation, if anything) will warn you if the memory falls below 150 MB, which for the most part, is a pretty important warning. Why? Because it will crash. Badly.

For the first three times I had to restart my VM, I assumed it was a one-off situation, probably won't happen again but guess what, it did. Everytime I uploaded an image or two at the same time (Ghost actually resizes down images > 2000px and also optimizes JPEGs, so that costs processing power), it would freeze up, to appreciate the Ghost admin interface devs, it still showed like it was working but when you try to SSH into the machine, good luck. Generally, you either wait until ghost detects the oopsie and restarts itself, or you force-restart the VM (which apparently also resets the public IP, something which I found out the hard way).

Solution? Just use AWS RDS. Like just don't host the database on the same machine if it's free-tier, just don't do it. And remember that when the entire world gave up on MySQL for PostgreSQL, the Ghost team stood by them. How's that paying off now, I wonder? 😎 But shoddy database jokes aside, remember that RDSes have a free-tier too! Personally speaking, I find Cloud SQL (that's the Google version) to be the best but it's also expensive, I don't know why, in any case I settled on using RDS solely because I don't want to mess around with authorization, you just put them in the same VPC, security group, whatever - and bam, it just works (for the most part).

The steps

Now, this is only relevant if you're deploying it yourself and if you're doing it the way I did, hell maybe by this time next year, Bezos has taken over the world with his super AI running on AWS but if not, follow the steps and you'll be set.

  • Check if EC2 and RDS are still there, if yes, you're in luck!
  • Make a new account with your card details, this is arguably the most annoying part of the process, depending on your bank and card, you might need to verify it with a small transaction, there's really no solid answers here.
  • Make a free-tier VM, when you're selecting the image to deploy, there should be a very helpful free tier selector on the left, select the latest Ubuntu LTS version.
  • Next you should see an option to select the type of instance, with a "Free tier eligible" in green.
  • On the storage page, you can select upto 30GB of memory (in free tier). Typically, that should get your VM running (in a bit). Remember to safely store your PEM file that AWS provides after you generate a keypair, you'll need this to log in later.
  • Now, time for that RDS. Open up the RDS menu, and launch a new database, select MySQL with the latest version and you should see a "Free tier" option in the templates, if not, try a couple of older versions.
  • Remember to note the administrator username and password (yes, please use a password).
  • When it comes to selecting VPC groups, remember to add the VPC group that was created when you created the VM, there should be a helpful dropdown so don't worry about it.
  • That's pretty much it, you shouldn't really change anything (if you want to be in the free-tier) without knowing what it does.
  • Let's use the PEM file to log in, if you're technically inadept or just plain lazy, the folks at AWS foresaw this and provided an entire interface to connect to your instance, just select the instance from the EC2 page, click connect and you should be able to see all the options.
  • If you're using the SSH option (as most of us do), all you have to do is paste the command they provide, looks something like ssh -i "pemfile.pem" [email protected] Alternatively, if you're using the console in AWS itself, I'm judging you a little bit, but let's continue.
  • Next, you want to follow the steps written by the Ghost people, with some caveats, which I will list below.
# Update package lists
sudo apt-get update

# Update installed packages
sudo apt-get upgrade

# Install NGINX
sudo apt-get install nginx

# Allow Nginx through ufw
sudo ufw allow 'Nginx Full'

# Add the NodeSource APT repository for Node 14
curl -sL | sudo -E bash

# Install Node.js
sudo apt-get install -y nodejs

sudo npm install ghost-cli@latest -g

# Create directory: Change `sitename` to whatever you like
sudo mkdir -p /var/www/sitename

# Set directory owner: Replace <user> with the name of your user
sudo chown <user>:<user> /var/www/sitename

# Set the correct permissions
sudo chmod 775 /var/www/sitename

# Then navigate into it
cd /var/www/sitename

ghost install

Refer to the official guide to understand the installation process.

  • You might have noticed that I skipped the "Install MySQL" section since there is no reason to set up the local database (because we will be using RDS).
  • When Ghost CLI asks you for the MySQL hostname, provide the public endpoint of the RDS, when it asks you for the username and password, provide the ones you set when you made the database.
  • Apart from this, the recommended instructions should work just fine.
  • Provided that Ghost CLI says everything works great and you can access the site on the listed public IP of the instance, you should be set! In case you set up SSL on a domain, it might throw off some security warnings when you open the site, but not a big deal really.

The extras

The bad thing about a 1GB 1 vCPU VM is the performance, among many other things  (don't worry about it really). So ideally, you want people to hit your origin server as less as possible and god forbid if someone did choose to DoS you, it wouldn't survive the wrath of an i5 8th gen laptop running Low Orbit Ion Cannon. So yes, you want to serve cached content, the best way is to use Cloudflare, they have a generous free tier and they provide a bunch of security features and content optimizations on top, and did I mention it's free? It's actually super easy and I highly recommend it over routing requests directly to your instance.

That only leaves one aspect of setting up your Ghost blog, emails! Provided you have full use of emailing subscribers, you ideally want to get your emails set up properly. The way that Ghost has set up their CMS is that you have to use Mailgun (see their reasoning here), and while their free trial only lasts three months, all usage < $0.50 is apparently never invoiced, in pure numbers, that's about 600 emails per month, which should probably last you a while. Once you have Mailgun all set up, you can just copy the SMTP credentials and add them to your config.production.json file like this, you'll also need to add the domain and API key in the Ghost admin interface to get Mailgun to actually deliver the newsletters as they are published.

  "mail": {
    "from": "'name' <[email protected]>",
    "transport": "SMTP",
    "options": {
      "service": "Mailgun",
      "auth": {
        "user": "username",
        "pass": "password"

The alternative would be the default setup with Nodemailer but from my experience, it doesn't work great and for some reason, Ghost doesn't like to use the Mailgun REST API for sending outbound emails from Ghost (other than newsletters, yes, I know it makes no sense).

Parting words

If it's worth preserving your data and the data of your readers, it's worth doing this. But mostly, this is for the people who want to self-host their content and make things work on their terms, seizing the means of control so to say. I think a lot of the work we do these days are so highly abstracted that we refuse to get into the nitty-gritty and understand the fundamentals, not as simple as not having to write a binary search tree when the library exists, but it's much more widespread than that.

So I'll tell you what, if you're still not convinced, try doing it yourself! And if you hate it, no love lost (maybe a bit of your hard-earned time). 😬