Migrating to Zola

16 Dec 2023

Migrating from Jekyll to Zola

4 minutes reading time


I started using Jekyll around 5 years ago and never needed another SSG. Its workflow is very straightforward and easy to tame on Linux/Mac (not to mention integrations with GitHub, etc). But a lot has changed, including restricted plugins. Now instead of debugging pages, I have to write ports for plugins or deal with rbenv. Let's fix this.


My legacy workflow was built around a single public repository pushing to GitHub Pages. This works great for a documentation/project site. But public drafts and cached source code make this look somewhat sloppy. So this time I decided to push it into a private repository. Currently, GitHub allows 2000 minutes of CI time per month on private repositories, which should be more than enough in my case.


name: Build


    runs-on: ubuntu-latest
      - uses: actions/checkout@v4
      - uses: shalzz/zola-deploy-action@v0.17.2-1
          BUILD_DIR: .
          BUILD_ONLY: true

    needs: build
    if: github.ref == 'refs/heads/trunk'
    uses: ./.github/workflows/deploy.yml
      TOKEN: ${{ secrets.GHP_TOKEN }}


name: Deploy

        required: true

    runs-on: ubuntu-latest
      - uses: actions/checkout@v4
      - uses: shalzz/zola-deploy-action@v0.17.2-1
          PAGES_BRANCH: trunk
          BUILD_DIR: .
          REPOSITORY: charlesrocket/charlesrocket.github.io
          GITHUB_TOKEN: ${{ secrets.TOKEN }}

This configuration utilizes GitHub's reusable workflows and allows one to manually trigger the build when required.


Wow, Zola! Very fast, packed with the essentials, and its Tera template engine is amazing!

Although porting my old theme wasn't hard at all, I failed to add a taxonomy prefix to posts; this is unsupported at the moment. But now I think I'll probably stick with shorter URLs anyway and expand categories, just like tags.

I wasn't sure how to approach CSP via meta tags (since GitHub does not allow server-side header tunings), but the built-in get_hash function works on compiled SASS files just as well.

page vs section along with the taxonomies, make some use cases quite confusing (there is a proposal to deprecate sections), but the current version is 0.17.2, so I will just follow the changelogs for now.


Looks very familiar:

{% block title %}
  {% if page.title %}
    {{ page.title }}{{ config.title }}
  {% else %}
    {{ config.title }}
  {%- endif %}
{% endblock title %}


Almost the same:

title = "Posts"
sort_by = "date"
template = "posts.html"
page_template = "page.html"

I'm not sure yet how I will be using non-post pages, but the standard layout from docs worked perfectly:


{% extends "base.html" %}

{% block content %}
<body id="posts">
  <div class="block-left">
    <div class="content">
      <a href="{{ config.base_url }}" class="logo"><img src="{{ get_url(path=config.extra.logo) }}", alt="logo" width="64" height="64"></a>
      <h1 class="section-title">{{ section.title }}</h1>
  <div class="block-right">
    <a href="{{ config.base_url | safe }}/tags" title="tags" class="dashed-top-link">tags</a>
    <div class="content">
      <ul class="posts-list">
        {% for page in section.pages %}
              <a href="{{ page.permalink | safe }}" class="post-title"><span>{{ page.title }}</span></a>
              <span class="date">{{ page.date | date(format="%d %b %Y") }}</span>
            <div class="post-info">
              <span class="word-count">{{ page.word_count ~ "w"}}</span>&nbsp;<span class="read-time">{{ page.reading_time ~ "m"}}</span>
            <p>{{ page.summary | striptags | truncate(length=150) | safe }}</p>
            <ul class="tags title-tags">
              {% for tag in page.taxonomies["tags"] %}
              <li><a href="{{ config.base_url | safe }}/tags/{{ tag | slugify }}">{{ tag }}</a></li>
                {% if not loop.last %}
                {% endif %}
              {% endfor %}
        {% endfor %}
{% endblock content %}

Pagination, tags, post summary - just like before, but better!

I probably should refactor all templates and utilize blocks better before moving my theme into a submodule (right after I finish touching up all the cool things that got implemented during this migration).