Skip to content

How We Built This Blog (and Broke It Twice First)

Ghost, a managed database, object storage, and a theme that lives in git. The stack behind this blog, including the two deploys that failed on the way.

We could have put the blog on a hosted platform in five minutes. We didn't, because we like owning the pieces. Here's what this blog runs on and why.

The stack

  • Ghost 6 for writing and publishing. It gives us a proper editor with image and video support, an admin area, and RSS and sitemaps out of the box.
  • A managed MySQL database on DigitalOcean for posts, settings, and accounts. Content lives there, so it survives every deploy.
  • DigitalOcean Spaces for uploaded images, served through a CDN. The app's container is replaced on every deploy, so anything written to its disk would disappear. Images need to live somewhere else.
  • A custom theme that matches the rest of the site, stored in the same git repository as everything else. Changing the design is a code change, reviewed and deployed like any other.
  • Transactional email through Resend, so Ghost can send sign-in verification messages from our own address.

It lives at /blog

The blog isn't a separate website. It's served from the same domain as the main site, at `fabricatedcrime.com/blog`. The platform routes anything under `/blog` to the Ghost container, and everything else to the main site. That keeps our search authority in one place and means visitors never notice a seam.

The two deploys that failed

Our first deploy of the blog failed. The build stopped because the Ghost base image no longer includes a small utility our Dockerfile relied on. A one-line fix.

The second deploy built fine and then failed to start. We had created the database cluster but never created the database user the app was configured to use. Also a quick fix, once we read the error.

Here's the part we like: neither failure touched the live site. The platform keeps the last healthy deployment running and rolls back automatically when a new one fails. The main site stayed up the whole time, which is exactly why we deploy this way.

What we took from it

  • Write down the dependencies you didn't know you had. Two of our three problems were things we assumed.
  • Make rollbacks boring. A failed deploy should be an inconvenience, not an incident.
  • Keep the blog's moving parts few. A database, an image bucket, and a container are easy to reason about at 2 a.m.

We come from film technology and streaming, so expect more posts like this. If you're curious how something on the site works, tell us and we'll write it up.