/ ghost

Hosting a Ghost blog for $5 a month

Create a cheap DigitalOcean do-it-yourself Ghost droplet

Not being much of a content creator, the most rewarding part of having my own blog is setting it up and configuring things to my liking. About a year ago, I converted my blog from a (barely used) WordPress instance to the latest coolness, a Ghost instance running on AWS Free Tier EC2.

It is amazing how fast a year goes by when one procrastinates on blog writing; it wasn't long before AWS emailed me to let me know that my freeloading was not appreciated (expiring). As a mini experiment and subconsciously a self-inflicted punishment, I kept the blog run for another month on the paid tier to see how much a blog with absolutely no web traffic would cost to host.

The result: ~$16/month; pretty expensive for a small blog. I had to hunt down a better solution and start over (yay), especially since Ghost had just reached its 1.0 release. Having heard good things, I decided on using a DigitalOcean one-click Ghost droplet but quickly found out that their smallest droplet at an affordable $5/month didn't support one-click Ghost installs (not enough RAM). I wasn't ready to take no for an answer.

After some Google-fu, I stumbled upon this guide by Dan Walker which provided a walk-though on how to create a $5 VPS droplet and squeezing in a Ghost install through the use of a 1GB SWAP memory. Brilliant! Well, mostly. The fairly comprehensive article leaves out a few details that might throw off those new to Linux and/or nginx so I am going to list a few important tips I've learned through trial and error.

Tips for $5/month DigitalOcean Ghost Droplet

Read this first: Running Ghost on a $5 Digital Ocean VPS
Then read the items below before starting the project

1. Use nano instead of vim

Both are text editors but nano is much more newbie friendly. Simply use nano filename in place of vim filename. All the commands you need are right there on the bottom (^X to save; it'll ask if you want to write to file).

2. Get SSL certificate for both www and non-www

Regardless of whether you're in the www or non-www camp for proper URLs, you should probably get SSL certificate for both sub-domains. You can do this by adding -d www.example.com to your certbot (LetsEncrypt) command before running it. In fact, you can add as many Subject Alternative Name (SAN) to the same SSL certificate by repeating the parameter. Like so: sudo certbot certonly --webroot --webroot-path=/var/www/ghost -d example.com -d www.example.com -d example2.com -d www.example2.com. Already ran the command before reading this? No problem. Just re-run it again properly, but add --expand to the end to update the certificate.

3. Point http to https for both www and non-www

After Ghost has been installed using the SSL option, it'll generate two nginx config files; example.com.conf and example.com-ssl.conf. You can simplify forwarding of www and non-www by editing the non-SSL config. Run sudo nano /etc/nginx/sites-available/example.com.conf and replace the content with the following:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;
    return 301 https://$host$request_uri;

Don't wory about the same file in /etc/nginx/sites-enabled/ as it is just a link to the same file so it'll be updated automatically.

4. Fix SSL certificate location

If you followed the original guide and created the SSL certificate first before installing Ghost, you need to point nginx to the right certificate location instead of the one Ghost made. Do so by editing the SSL version of the config by running sudo nano /etc/nginx/sites-available/example.com-ssl.conf. Update the ssl_certificate and ssl_certificate_key rows to the right location (returned by the certbot command); you can comment out the original rows out as shown below with # or just replace the rows:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.com www.example.com;
    root /var/www/ghost/system/nginx-root;

    #ssl_certificate /home/ghost/.acme.sh/example.com/fullchain.cer;
    #ssl_certificate_key /home/ghost/.acme.sh/example.com/example.com.key;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /var/www/ghost/system/files/ssl-params.conf;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;


    location ~ /.well-known {
        allow all;

    client_max_body_size 50m;

In case you missed it, rows 8-9 were commented out and rows 10-11 added. The rest of the config file should look similar to the default one Ghost CLI made.

5. Add Yarn public key

This one isn't necessary for getting Ghost running, but if you ever run apt-get update to update applications in the future, you will get an error because the original guide did not include a command to add the Yarn public key. Fix this by running curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - to save some headaches down the road.

6. Don't forget to reload nginx

Once you've completed everything, don't forget to test and restart nginx via these commands, respectively: sudo nginx -t and sudo service nginx restart.

Final note: If you enjoy running one Ghost blog on a $5 DigitalOcean droplet, you'll really enjoy doubling that saving by running two (or more) ghost blogs on the same droplet; post coming soon! Otherwise, I hear you can also host one for free via Heroku (not personally tested).