My personal website v2.0

Photo credit: Thought Catalog

Let’s first talk about v1.0

The previous version was a proof of concept. I wanted to run on Cloud Run (, with Sapper as a static site generator for the frontend, so that the Ghost database did not have to be accessed at runtime.

The architecture looked like this:

v1.0 architecture

What were the challenges?

Publication process

To publish a new blog post, I had to:

  1. Start the Cloud SQL databaes used by Ghost.
    • It’s worth noting I have could left that turned on, but the point was not to have a database at runtime ;-)
  2. Log in to the Ghost instance.
  3. Write the post and upload assets.
    • Asking for reviews was tricky. I would have to give reviewers access to Ghost and they would see the blog post displayed in Ghost, not the way it will look once deployed.
  4. Publish the post.
  5. Wait for the static website to be generated and deployed to Firebase Hosting.
  6. Shut down the Cloud SQL instance.

As I mentioned well before I developed v1.0, this was over-engineered and overly complex.

Operating costs

The operating costs were about CAD $40 per month:

v1.0 GCP costs

Throughout the first half of 2020, the blog had ~2,000 unique visitors per month with ~5,200 page views. I wanted to find a more cost effective way to provide my content to others.

Google Analytics

I used Google Analytics and Firebase Performance tracking. While the insights are interesting, I rarely looked at more than number of visitors, the country they are from and which blog posts they read. The various scripts I needed to get this to work also came at a performance cost I want to avoid in v2.0.

The goals for v2.0

Before starting the project, I defined the following goals:

  • Open
  • Best practices
  • Automation
  • Low operating costs


I want all aspects of my website to be open. The source code including project management, pull requests, issues, etc.

Each blog post will be released as a pull request. Anyone can access drafts and provide feedback. This also gives me the flexibility to potentially allow for community contributions such as translations etc.

In addition, I will make the website’s analytics available publicly as part of a project later in 2020. Stay tuned for that by following me on X @mootoday.

Best practices

As it has always been the case, my personal website is a place for me to experiment with new technologies. If I get asked “How would you …?” in relation to web development, I want my answer to be “Have a look at”.

A perfect Lighthouse score, fully accessible, top SEO ratings, works without JavaScript, etc.


For anyone who’s worked with me they know that automation is near and dear to me. “Don’t document what you can automate!” The focus is on using NPM scripts where necessary and connect it all via GitHub Actions.

Each PR provides a preview URL and once merged, a production deployment happens within seconds.

To create a new blog post and all necessary directories, I use npm run generate and answer a few questions.

Operating costs

As close to $0 as possible and no database since it’s all static content - simple.

What’s new in v2.0?


It’s a lot simpler:

v2.0 architecture

Diagram source:,jY4N1Wo4Vgqn45E9uxUqPA

All we’ve got is:

  • for my cloud-based development environment.
  • GitHub for the blog posts, source code and workflow automation.
  • Vercel for hosting.

The publication process now looks like this:

  1. Write the blog post and add assets.
  2. git push it to GitHub and open a pull request.
  3. Share the draft URL with reviewers; iterate on the post.
  4. Merge the PR.
    • Deployment to production then happens automatically.

Interactive blog posts written in mdsvex

This is arguably a bit of a nice-to-have. It is so nice though that I have to point it out!

Let’s look at an example on the next line:

You've been on this page for 0 seconds

Alright, not bad. I write this blog post in mdsvex which is basically Markdown with Svelte mixed in wherever I want.

Another example, taken from

In the blog post file, the following is what I write:

	import Clock from './clock.svelte';

... Another example, taken from

<Clock />

I’m looking forward to taking advantage of that as I get back into more frequent blogging.

Draft preview URLs

Have a look at the recently closed blog post pull requests, open one and find the preview URLs to see how the post looked when it was in draft.

With the blog post’s content available in the pull request, reviewers can comment on individual lines and I can pick up the conversation right there in the correct context. No more back & forth via emails or copy & paste from shared Google Docs.

What’s next?

Personal website & blog

I track the v2+ project on GitHub. The focus is going to be on small enhancements and experiments as I see fit.

Book: Cloud Native Web Development

On June 28, 2020, I (will release) released my book Cloud Native Web Development. It’s two decades of web development experience packed into 200+ pages and 19 pull requests. It’s a hands-on guidebook from zero to production and anything in between!


Was this helpful?