Skip to content
← All posts
5 min read Dawid Skłodowski

A developer blog with Jekyll and GitHub Pages

Why a static site is the right tool for a developer blog, and how to build one with Jekyll and host it free on GitHub Pages — front matter, Liquid layouts, Markdown posts, and writing in the same workflow you already use for code.

If you are a developer who wants a blog, the reflex is to reach for WordPress — and it is the wrong reflex. A blog is, at heart, a pile of articles and a bit of navigation. It does not need a database, a PHP runtime, a login page, or a security patch every other week. What it needs is to render fast, never go down, cost nothing, and let you write the way you already work. That is exactly what a static site generator gives you, and for the Ruby crowd that means Jekyll on GitHub Pages.

We built our blog this way, and the setup has a particular elegance: your writing lives in the same git repository, in the same Markdown-and-text workflow, as the code you write all day. Here is how it fits together.

Why static, and why this stack

A static site generator takes templates and content files and compiles them, once, into plain HTML, CSS, and JavaScript. There is no server-side code at request time — a visitor is served a pre-built file. The consequences are all upside for a blog:

  • It is effectively un-hackable. There is no database to inject, no admin panel to brute-force, no plugin with a CVE. The attack surface of a folder of HTML files is close to zero.
  • It is fast and cheap. Static files are trivial to cache and serve; a CDN or GitHub Pages delivers them instantly, for free, at any traffic level.
  • It versions like code. Posts are files in git. You get history, diffs, branches, and pull requests on your writing — draft a post on a branch, review it, merge to publish.

Jekyll is the canonical Ruby choice and — crucially — GitHub Pages builds it for you natively. Push Markdown to your repository and GitHub compiles and hosts the site automatically. No build server, no deploy script.

The shape of a Jekyll site

A Jekyll project is a handful of conventions. The important pieces:

_config.yml        # site-wide settings
_layouts/          # HTML templates (default.html, post.html)
_includes/         # reusable snippets (header, footer)
_posts/            # your articles, one Markdown file each
index.html         # the home page
assets/            # CSS, images, JS

The convention that matters most is _posts/. A post is a Markdown file named by date and slug — 2014-10-15-my-first-post.md — and Jekyll reads the date and title straight from that filename and the file’s header.

Front matter and Markdown: the post itself

Every content file starts with a block of YAML “front matter” between triple dashes, followed by the body in Markdown:

---
layout: post
title: "Why I switched to Jekyll"
date: 2014-10-15
tags: [jekyll, blogging]
---

Here is the **actual post**, written in Markdown. Code blocks just work:

    def hello
      puts "world"
    end

And so do links, lists, and images — the things a developer post is made of.

The front matter is metadata Jekyll uses to render the page: layout picks which template wraps it, title and date and tags are available to the templates as variables. The body is plain Markdown — which is the whole point. You write a post in the same lightweight markup you already use in READMEs and pull requests, in your own editor, with no WYSIWYG fighting you over a stray <p> tag.

Liquid: templates and loops

Jekyll renders templates with Liquid, a small, safe templating language. A layout wraps your content and pulls in metadata; a loop builds your index from the posts collection:

<!-- _layouts/post.html -->
<article>
  <h1>{{ page.title }}</h1>
  <time>{{ page.date | date: "%B %-d, %Y" }}</time>
  {{ content }}
</article>
<!-- index.html: list every post, newest first -->
<ul>
{% for post in site.posts %}
  <li>
    <a href="{{ post.url }}">{{ post.title }}</a>
    <span>{{ post.date | date: "%Y-%m-%d" }}</span>
  </li>
{% endfor %}
</ul>

{{ ... }} outputs a value, {% ... %} is a tag (control flow), and the | applies a filter — here date formats the timestamp. site.posts is the collection Jekyll builds from your _posts/ folder, sorted newest-first. That half-dozen lines is your entire home page, and it stays correct forever: write a new post file and it appears in the list automatically.

Local preview and publishing

You run the site locally exactly as it will be served:

gem install jekyll
jekyll serve            # builds the site and serves it at localhost:4000, live-reloading

Edit a Markdown file, save, refresh — the same tight loop you have when working on code. Publishing is just git:

git add _posts/2014-10-15-my-first-post.md
git commit -m "New post: why I switched to Jekyll"
git push origin gh-pages     # GitHub Pages builds and deploys automatically

Push to the Pages branch and your post is live in under a minute, served from GitHub’s infrastructure at no cost. Point a custom domain at it with a CNAME file and you have a real blog on your own URL without paying for or maintaining a single server.

The honest trade-offs

Static is not free of cost, just a different set. Comments need a third-party service (Disqus and friends) since there is no backend — though for a developer blog many people happily skip comments entirely. Anything truly dynamic — search, forms, personalisation — has to be done client-side or handed to an external service. And the build is ahead-of-time, so a thousand-post site takes a moment to compile (rarely an issue at blog scale).

None of that outweighs the wins for the use case. For a developer blog the static approach is almost perfectly matched to the job: your writing lives beside your code, in git, in Markdown; it publishes by git push; it is fast, free, and secure by construction; and the whole thing is plain files you fully understand and own. We have never regretted choosing it, and the only maintenance in years has been writing the next post.