How to set up this blog with Jekyll and GitHub pages

Outline

On GitHub Pages you can host websites like this one for free. There are tons of tutorials out there, here I’m just sharing how I set up this very blog you’re reading.

By default GitHub Pages uses Jekyll for building your website but they will also accept already built static files or your own workflow for building your website.

In this post I am using Jekyll and the template alembic. I will show how and what I modified to make my site.

Installation and setup

We will be using Jekyll and bundler so we can locally preview the website while working on it.

To install Jekyll we’ll first install ruby. On Linux or Windows Subsystem for Linux (WSL) we install like this:

sudo apt install ruby-full build-essential zlib1g-dev

On a mac we do:

brew install ruby

Next we install bundler which we will use to manage the ruby plugins.

gem install bundler

We’ll make our project directory and call it my_jekyll_website

mkdir my_jekyll_website
cd my_jekyll_website

To setup our project we record our dependencies in the Gemfile.

Gemfile

source 'https://rubygems.org'
gem "github-pages", group: :jekyll_plugins
gem "jekyll-remote-theme"

We are using the github-pages plugin so we can test locally. The jekyll-remote-theme plugin lets us use themes other than those preconfigured for GitHub Pages. We will show in the section adding first content how to use a remote theme.

Next we’ll add jekyll to the Gemfile like so:

bundle add jekyll

This will pin a specific version of jekyll, in my case 3.10.

Now we install all the dependencies

bundle install

Quick Setup

The remainder of this post is about setting up elements piece by piece, explaining what they do and how to modify them. If you want to just get started right now and change things later, you can download the GitHub Pages with remote theme kit linked to on the theme’s GitHub. This kit uses reasonable defaults and enables most features from the get-go. You can still always come back to my post and read about how to further modify the theme for your needs.

Minimal viable website

Starting jekyll server

If we want jekyll to start serving a webpage from our directory, we run the following command.

bundle exec jekyll serve --watch

This will keep jekyll running and refreshing as we make changes. The initial output should show us something like this:

To use retry middleware with Faraday v2.0+, install `faraday-retry` gem
            Source: /home/myuser/my_jekyll_website
       Destination: /home/myuser/my_jekyll_website/_site
 Incremental build: disabled. Enable with --incremental
      Generating... 
                    done in 1.573 seconds.
 Auto-regeneration: enabled for '/home/myuser/my_jekyll_website'
    Server address: http://127.0.0.1:4000
  Server running... press ctrl-c to stop.

This also creates a directory called _site which jekyll has populated with an assets folder:

ls -la _site
total 12
drwxrwxr-x 3 tobsecret tobsecret 4096 Feb  5 22:33 .
drwxrwxr-x 3 tobsecret tobsecret 4096 Feb  5 22:33 ..
drwxrwxr-x 3 tobsecret tobsecret 4096 Feb  5 22:33 assets

If we now go to http://127.0.0.1:4000 we see jekyll is serving up the file structure of our _site directory: Minimal Jekyll Website Screenshot

This _site directory can have a lot of files in it, so we’d like to not have those show up when we use git for version control, so we’ll make a .gitignore file to ignore this and a few other artefacts that Jekyll can create:

.gitignore

_site
.jekyll-cache
.sass-cache

Adding first content

I am using a Jekyll template called Alembic for my blog which comes with a nice default setup. In our _config.yml we’ll tell Jekyll to use the jekyll-remote-theme plugin and specify the remote_theme: daviddarnes/alembic@main. In the navigation_header section we’ll specify two sections: Home and Blog. For our webpage this will generate a Home and a Blog link at the top of every page in the navigation header.

_config.yml

title: Tobsecret's homepage
description: Programming Blog & Dominion sets
plugins:
  - jekyll-remote-theme
remote_theme: daviddarnes/alembic@main

# 9. Site navigation
navigation_header:
- title: Home
  url: /
- title: Blog
  url: /blog/

The paths in the navigation_header section specify the path in our directory where jekyll should be looking for a file called index.md that has the content for the page.

So for our Home page it will look in /, so directly in our project directory.

index.md

---
layout: page
title: Home
---


---

Latest blog posts:
{% for post in site.posts limit:5 %}
- [{{ post.title }}]({{ post.url }}) — {{ post.date | date: "%Y-%m-%d" }}
{% endfor %}

layout: page is important since Alembic predefines the layouts for page and post, i.e. those pages will get the navigation header and other features of Alembic.

The little loop in the index.md grabs the latest 5 posts from the _posts folder by their date field.

If we refresh our page, it should now look like this: minimal_config_alembic_screenshot.png Notice the header now has two links, one to Home and one to Blog.

If we clicked on Blog we would get a 404: File Not Found.

To populate that page we’d have to make another page blog/index.md which I’d just copy what the template uses:

---
layout: page
title: Blog
permalink: /blog/
---


{% if site.posts.size == 0 %}
No posts yet. Create posts in the `_posts` folder.
{% else %}
<ul>
{% for post in site.posts %}
  <li><a href="{{ post.url }}">{{ post.title }}</a> — {{ post.date | date: "%Y-%m-%d" }}</li>
{% endfor %}
</ul>
{% endif %}

empty_blog_screenshot.png

Now if we wanted to add our first post, so something actually shows up in the blog, we want to create a file _posts/2026-02-15-first-post.md:

---
layout: post
title: "Writing helps me think"
date: 2026-02-15 12:00:00 +0000
---
When I write about the software I write, I find myself looking up 
a lot more questions than when I am just getting things done.

And now our blog page is updated: updated_blog_post_view

You can similarly also copy the Search section by adding a section in the navigation_header section of your _config.yml:

navigation_header:
- title: Home
  url: /
...
- title: Search
  url: /search/

and copying over the search/index.md:

---
title: Search
excerpt: "Search for a page or post you're looking for"
---

{% include site-search.html %}

Updating preferences

Updating (S)CSS

Usually to update files that the template has pre-defined, we have to provide an alternative copy of that file.

To update the site’s styling via SCSS everything goes through assets/styles.scss which looks like this in the template default:

---
title: false
styles: true
---
@import "alembic";

When Jekyll processes this Sass (.scss), @import "alembic" generates all of the style elements included with the template. This means if we want to change any of those, we have to import or explicitly call them before alembic is imported.

Let’s change the colors via a file _sass/modified_colors.scss. Putting it in the _sass folder means Jekyll will find it when we try to import it.

_sass/modified_colors.scss.

$linkColour: #61AFFF; // Modulex Medium Blue
$hoverColour: #0055BF; // Blue 
$accentColour: #61AFFF; // Modulex Medium Blue

We also have to modify assets/styles.scss to import the colors before alembic gets imported:

assets/styles.scss

---
title: false
styles: true
---
@import "modified_colors";
@import "alembic";

Any additional SCSS can be imported below alembic, e.g. for my dominion section I wrote _dominion.scss to standardize the formatting of the dominion cards and the containers for the expansions.

Adding an RSS feed

RSS feeds can be used to automatically get updated when a new post is published via an RSS reader like Feedly. In Jekyll we only have to add this little line to our _config.yml

plugins:
  ...
  - jekyll-feed

Now our RSS feed is automatically generated and available at <domain>/feed.xml (i.e. tobsecret.github.io/feed.xml).

By default the template sets up the footer to contain the same navigation as the header, or optionally a clickable link with some text. The template also features a way to include social links and having them displayed in an aside. For my blog I wanted these socials to instead show in the footer.

First let’s set up our socials in _config.yml as defined by the template:

social_links:
  LinkedIn: https://www.linkedin.com/in/tschraink
  GitHub: https://github.com/tobsecret
  RSS: /feed.xml

All we need to do is to create _includes/nav-footer.html with this content:

{% if site.navigation_footer %}
<nav class="nav  nav--footer">
  <ul class="list list--nav">
{% include nav-social.html %}
  </ul>
</nav>
{% else %}
  {% include nav-default.html %}
{% endif %}

The template provides its own _includes/nav-footer.html which instead of just {% include nav-social.html %} includes some logic for handling the text + external link the template would use.

We also have to enable this footer in the _config.yml like so:

navigation_footer: true

And we can now see the socials in our footer: footer_with_socials.png

Adding a feature image

I spent some time to model and render my banner image in Blender and wanted to add it to each post. My banner is assets/banner.webp. We can add this for all posts by adding the following to our _config.yml:

# 5. Collections
defaults:
  -
    scope:
      path: ""
      type: "posts"
    values:
      layout: post # Set the default layout for posts
      feature_image: "/assets/banner.webp"
  -
    scope:
      path: ""
      type: "pages"
    values:
      layout: page # Set the default layout for pages
      feature_image: "/assets/banner.webp"

This won’t update immediately in your browser so you’ll have to interrupt your bundle exec jekyll serve --watch call with CTRL+C and then restart it. Refresh the page and you should see the banner at the top.

You can still manually override it in any particular page or post by specifying a feature_image in the markdown:

---
layout: page
title: Blog
permalink: /blog/
feature_image: /assets/some_other_banner.png
---