The tech behind my website

  • Guides & Tutorials

Digging into the technology and services I use on my website, influenced by the ultimate goal of getting me writing again.


The eternal dilemma whenever I build a new website is deciding what stack to use. Do you go for a hot new static-site generator? What about old-faithful WordPress? Or should you go no-code and use something visual like Webflow?

I’ve been guilty in the past of allowing my desire to learn and tinker to influence my decision by leaning towards approaches that involve learning a new technology, or getting more hands-on with the core systems. This is great for learning, however it’s absolutely disastrous for writing on a regular basis.

As I’ve grown older the way I direct my time and energy has shifted, generally wanting to spend as little time as possible on things that don’t get me any closer to my goals. The perfect solution is to avoid having to do something entirely, either by using something pre-made or by paying for a hosted service. In both cases I get all that time and energy back to focus on the things that really matter to me.

I have the following goals for this website:

  • Get me writing on a regular basis again
  • Make it easy to post media-rich content
  • Draft and publish content from a rich WYSIWYG editor
  • Avoid writing code and running build-steps for publishing content
  • Respect privacy as much as possible (eg. fewer cookies, analytics)
  • Use as much existing technology as possible
  • Keep costs low, especially when needing to grow

This post goes over the technology stack that I settled on for the creation of this site, using those goals as a filtering method.


Stack overview

Ghost

Running cost: free (if self-hosted)

Selecting a platform for the content of the site boiled down to whether I wanted a static site (eg. Gatsby), a dynamic site (eg. Ghost) or something in between (eg. Gatsby with Ghost running headless).

Previously I had used Gatsby, and most of the reason I stopped writing in the past was due to there being too much overhead with publishing content as a result. For the refresh I wanted something that had as few barriers to entry as possible and that meant something self-contained and dynamic, effectively WordPress or Ghost.

I went with Ghost for a few reasons:

  • It’s lightweight and very easy to get up and running
  • I’ve used it before and had a positive experience with it
  • It has no plugins so I won’t be tempted to bloat it with stuff I don’t need
  • The writing experience is conducive to publishing quickly and easily
  • The built-in members functionality is useful for running a newsletter without having to use extra services (aside from Mailgun)
  • It’s extremely cheap to run if you take the self-hosted route

Ghost isn’t perfect though:

  • Support for external images is lacking (no galleries, no responsive images)
  • Members functionality is generally either on or off with little in-between
  • If you aren’t happy with the base functionality there’s not much you can do about it aside from editing the core files (the downside to having no plugins)

Fortunately, most of the downsides can be worked around using other methods like custom HTML in the blog posts, Cloudflare Workers or third-party services.

DigitalOcean

Running cost: ~£4.00 ($5.00) per month

Selecting a host is a simple decision for me — if it’s a static site it goes on Netlify, and if it’s anything else it goes on DigitalOcean. There are a few exceptions, though that’s the general rule.

There’s nothing bad about alternative options, it’s just that I’ve always used DigitialOcean and had a positive experience with them through the years. Plus their prices are very affordable for what you get.

What’s also a massive plus-point for this particular case is that they have a one-click Ghost install, so getting the site up and running took significantly less time than doing it all from scratch.

There are a few downsides to self-hosting with DigitalOcean though, especially vs. using Ghost’s pricier hosted option:

  • You are responsible for maintaining the server and keeping it updated and secure
  • There is no caching by default so bandwidth usage (and costs) can rapidly increase if you use a lot of images or have a popular blog
  • DigitalOcean backups/snapshots aren’t encrypted nor performed daily

I worked around the caching and bandwidth by putting Cloudinary and Cloudflare in front of the site, and the backups have been resolved by using SnapShooter and B2.

SnapShooter and B2

Running cost: free (within limits)

I needed a better solution for backups given that DigitalOcean snapshots are not appropriate for my needs (eg. not encrypted, only weekly).

After a bit of research I found a service called SnapShooter which can handle daily backups through DigitalOcean, and can even handle file-level and database backups without having to back up the entire server.

What’s also great about SnapShooter is that they have a Ghost-specific offering that automated most of the setup process, and they support sending the backups directly to B2 (or other providers) so I can keep everything encrypted in a location that I trust.

They are also really friendly and added a new feature to the Ghost backup service after I got in touch with their support — now the config files are backed up too!

In terms of costs — SnapShooter has a free plan that the Ghost backup falls within and B2 is free for the first 10GB, which you’ll be hard-pressed to hit with Ghost. Even if you use more than 10GB the costs for B2 are very small.

Cloudinary

Running cost: free (within limits)

The quickest way to chew through storage and bandwidth allowances is to host your own media files, especially if you want to share high-res photography. Even if you only had 10MB of photos in a post that would still be 1GB of bandwidth for 100 visits! Multiply that by however many posts you have and your storage and bandwidth can quickly disappear.

This is compounded once you start generating images at multiple resolutions to support modern features in browsers like responsive images. In that case, you’ll need to store not only the original large-scale image but also the copies of that image in various resolutions. Let’s not do that.

I chose Cloudinary to solve this for me for the following reasons:

  • They have a powerful Web UI for uploading and managing media
  • You get a generous allowance of media storage under the free plan
  • You get a decent bandwidth allowance under the free plan
  • They handle image resizing at the time of a request, so you don’t need to do this yourself nor store copies of images
  • Images are served via a CDN so performance is improved

One thing that I’ve done differently with Cloudinary is that I don’t request images directly from them, and instead proxying and caching them using Cloudflare Workers.

This dramatically reduces the amount of bandwidth that needs to go through Cloudinary and ensures that I can remain within their free plan indefinitely. It also means that I get to use a custom subdomain for the media files at no cost.

In this setup, Cloudinary is the media host handling dynamic image resizing, and Cloudflare is the caching and performance layer on top of that.

Vimeo

Running cost: £6.00 ($7.00) per month

While Cloudinary is perfect for image-based media, it’s not something that I would use for video files (though it does support video). Part of the reason for this is that you can’t cache video using Cloudflare Workers and so I would quickly eat through the free Cloudinary allowance if I used them to serve video files.

I did a lot of research into options for affordable video hosting that isn’t a pain in the arse to manage, and it came down to two main options: YouTube or Vimeo.

I went with Vimeo in the end for the following reasons:

  • It’s less data that I’m sharing with Google
  • You have a lot of control over how to serve and display videos on your site
  • You get “unlimited” bandwidth (fair-use limits)
  • You can limit video embeds to your own domain
  • You can be kind to visitors and disable analytics
  • They specifically allow you to use them as a generic video hosting service (eg. for short videos used in the background of your site)
  • Vimeo (and YouTube) is significantly simpler than rolling my own video CDN

You could probably get away with using their free plan or using another service, however I’d rather spend a small fixed amount per month for unlimited bandwidth and knowing that I won’t be kicked off for storing generic video files.

Mailgun

Running cost: ~£0.60 ($0.80) per 1,000 emails

If you want to use the baked-in members and newsletter support in Ghost then you’re required to use Mailgun. You don’t currently have a choice in this as it’s the only email provider supported by Ghost for newsletters.

While Ghost handles the management and storage of email addresses and newsletters, Mailgun is used as a bulk emailing service to send those newsletters en-masse without triggering all sorts of spam protection within email providers.

They have an EU region (again, preferred) and the “Flex” pay-as-you-grow plan seems reasonable given the number of emails that I anticipate sending ($0.80 per 1,000 emails). If I ever got to the point of sending 50,000 emails per month then it’ll cost $35 per month on the next plan, which is still reasonable.

You can also check out Revue if you want a completely free option for newsletters and don’t mind not using Ghost to manage it all for you.

UK Postbox

Running cost: £1.20 per letter, or unlimited for £9.00 per month

One of the requirements of running a newsletter is that you need to have a physical address. Given that I don’t like giving out my home address I decided to look for an alternative to this.

I settled on UK Postbox, which is a service providing a virtual physical mailing address. This isn’t just a PO box service, they also offer services for receiving parcels on your behalf and even as a proxy address for registering a business.

I’ll likely upgrade to the £9.00 per month plan so I’m not punished on a pay-as-you-go basis if any spam mail is sent to my address. I still think this is worth it as I can use this address for all sorts of situations where I want to respect my privacy.

Plausible

Running cost: £6.00 (~$8.00) per month for 10,000 pageviews a month

Deciding which service to use for analytics has been tricky, especially as I want to respect privacy and avoid using tracking cookies as much as possible.

The main options for privacy-centric analytics seem to be:

They’re pretty similar but I ended up settling on Plausible as they have a slightly cheaper monthly plan, given that I don’t anticipate needing to track more than 10,000 pageviews a month for a while. Even if I do need that, Plausible is still roughly the same price at each subsequent level.

I’m not entirely convinced I need nor care about analytics enough to keep using it forever, though I’ll keep using it for the short-term and see how I get on. Either way, I feel much happier using Plausible than I would be using Google Analytics.

Cloudflare

Running cost: free within usage limits

Now for Cloudflare, the glue that holds together the entire stack.

I use Cloudflare for a variety of purposes, specifically:

  • DNS for managing my domain and subdomains
  • Edge-cache for serving static content as quickly as possible and reducing the load on DigitalOcean and Cloudinary
  • Proxy for serving Cloudinary media through a custom subdomain (eg. media.robinhawkes.com)
  • Extending the functionality of Ghost by rewriting and caching HTML at the time of each request

And how much does it cost for all of that? Absolutely nothing! 🤯

I wanted to touch on the last use-case for Cloudflare as it’s a little odd, though it’s incredibly powerful. In fact, it’s saved me from having to do any customisation of the Ghost codebase to work around limitations with responsive images for external media, amongst other things.

It’s all achieved using Cloudflare Workers, which are little scripts that you can run on the Cloudflare edge cache. These scripts can do all sorts of cool stuff, though in my case I’m interested in two features:

  • Rewriting HTML before it reaches the cache/browser
  • Caching images from Cloudinary (only allowed in Cloudflare Workers)

The HTML rewriting is most interesting as it allows you to manipulate the output from Ghost before it reaches your visitors. This means that you can work around some of the more frustrating aspects of Ghost like the lack of responsive images for external images.

Effectively what I have done is to create a little worker script that reads the HTML output from Ghost and looks for any external images that are from Cloudinary. If it finds any it rewrites the image elements, adding a srcset attribute with dynamic Cloudinary endpoints to transform the image into smaller dimensions. It’s a little messy but the end result is clean and it works, so it’s good enough for now.

Is this any better than before?

In short, yes! After all, I’m writing and publishing this post so that’s a success.

The long answer is that there has certainly been a lot more effort involved in getting something up and running with Ghost compared to throwing together a static site. However, that’s because it naturally comes with a lot of baked-in features that need configuring, and once they are set up you shouldn’t need to touch them again.

From now on I should be able to focus on writing and publishing content without having to concern myself with managing a deployment stack or the inevitable tinkering that comes with running a static site.

It feels good to be writing again.