New Year, New Blog

Happy New Year!

It's a new year and I'm changing jobs so have found myself with some spare time! I decided to use some of that time catching up with some of my 'tech projects' list which has been sadly neglected over the past 12 months.

One of those items was moving my blog from a paid Wordpress plan to a free solution hosted on GitHub pages (free) and using a headless content-management-system (mostly free).

I decided to use Gatbsy (a React based framework with rich data integration for building static sites) and the Contentul headless CMS. There are lots of other headless CMS options out there, but I started with Contentful and therefore I stayed with it.

GitHub pages

GitHub Pages is a static site hosting solution built right into GitHub. All you need to do is create a repository called (username).github.io and put your site's code in there. Shortly afterward your site will be available at https://(username).github.io. It also supports custom domains (with SSL certificates). This hosting is totally free :)


Gatsby is a React based framework which allows you to quickly build optimised static sites which use data sources like a CMS or markdown files. It is very easy to bind to these data sources and uses graphql to query data sources including the filesystem. It is really easy to get started:

npm install -g gatsby-cli
gatsby new gatsby-site
cd gatsby-site
gatsby develop

After doing this you can visit localhost:8000 and see your new gatsby site running.


Contentful is a headless CMS where you can define content-models (think of them as schemas) and create entries in those content-models (think of them as records). Contentful also provides asset management for images and other media and a global CDN to allow them to be used. It has a free account which will be perfect for a small blog.

A headless CMS is a place where you can manage the content for a blog e.g. you can create a content-model which describes a basic blog article and then create entries for each blog article. It has a plugin for Gatsby which allows Gatsby to import these entries while the site is being built.

Putting it all together

This site runs from two GitHub repositories:

  • one private: this contains the source code to drive Gatsby
  • one public: this contains the output of the build process and sits behind rjk.codes (which is hosted by GitHub pages)

Custom domain on GitHub pages

Adding a custom domain to your GitHub pages site is easy, you simply create a file called CNAME containing the name of your domain on a single line e.g. example.com and put it in the root of your repository. Once this file is commited GitHub will pick it up and generate the SSL certificate in the background (this can take some time). Then point your domain (via a CNAME record) to (username).github.io. If your domain is an apex e.g. example.com you'll need to use a service that supports CNAME flattening (like Cloudflare) or create an A record pointing to the correct IP addresses.

There are more details on this here.


I'm using GitHub actions build the site and deploy it. GitHub actions allows you to run workflows and automation, a lot like TravisCI but it is built into GitHub. It is free up to 2,000 minutes per month for private repositories. To create a workflow you create a YAML file in the repository under the following path in your repository: .github/workflows and put something like the following in it. This example uses node 12.x to install all the dependencies needed to build the site, builds the site and then uses the peaceiris/actions-gh-pages@v2.5.0 action to commit the site to the repository used to host the static pages.

name: Build & Deploy Blog
on: [push, repository_dispatch]
    runs-on: ubuntu-latest
        node-version: [12.x]
    - uses: actions/checkout@v1
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
        node-version: ${{ matrix.node-version }}
    - name: Install dependencies
      run: npm install
    - name: Build
      run: npm run build
    - name: Deploy
      uses: peaceiris/actions-gh-pages@v2.5.0
        EXTERNAL_REPOSITORY: ***/***.github.io
        PUBLISH_BRANCH: master
        PUBLISH_DIR: ./public

There's lots of documentation available here on how to use GitHub actions.

Of course, a change in the site source code is not the only thing that needs to trigger a new build of the blog site, changes in content need to do this as well. Luckily for us, GitHub exposes a method to trigger an action via a Webhook and Contentful allows you to define Webhooks which are called when particular actions are taken.


This is a preview feature and it could change without warning

GitHub allows you to trigger an action based on the repository_dispatch event more details here. You can trigger repository_dispatch events with a webhook call, and this makes them perfect for our needs more details here.

Make sure that your workflow yml file has the repository_dispatch event in the on property and then in Contentful go to Settings > Webhooks > Add Webhook. Use the following settings:

Setting Value
Name A value of your choice
URL POST https://api.github.com/repos/[USERNAME]/[REPOSITORY]/dispatches
Triggers Entry: publish & unpublish
Custom Headers Accept: application/vnd.github.everest-preview+json
Custom Headers User-Agent: contentful
HTTP Basic Auth Header Click on '+ Add HTTP Basic Auth Header' and in username put your GitHub username and in password put a GitHub Personal Access Token. See here for details on how to create a personal access token if you don't know how.
Automatically compute the value of the Content-Length header yes
Payload { "event_type": "trigger-build", "client_payload": {}}

Now, each time you either commit code to your private repository (change the site) or publish/unpublish content, it will trigger a build of your site and the changes will be made available within a few minutes.

All done

At the end of this we have pretty much free blog. The only cost is registering your domain and any ongoing DNS service charges. I use AWS Route53 but you could easily minimise these costs and use something like Cloudflare's free DNS service (they will still charge to register the domain though).

-- Richard, Jan 2020