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.