My Blog Setup with Jekyll, Org-mode and GitLab Pages

Associated Youtube Video: https://youtu.be/6feWmRBdL9c

Associated Odysee Video: https://odysee.com/@LibrePhoenix:8/i-made-a-blog-with-jekyll,-org-mode-and:8

This is a brief overview on how I got this website to work and how you might do something similar!

Tools Needed

  • Bundler: Used to manage Jekyll and related Ruby dependencies (Jekyll is a Ruby program)
  • Emacs with Org-mode: Used to do the actual writing and text-editing!
  • GitLab Pages: CI/CD run on GitLab to host the static site
  • Any Domain Name Registrar (optional): If you want to use your own domain

Jekyll Setup

Install Bundler

Start by installing bundler to your development machine, which lets you set up virtual environments for Ruby. It should be in every distro’s standard repos. This is necessary because Jekyll and its dependencies are written in Ruby.

Setup Project Structure

Next, we setup the directory structure. You can follow the excellent guide found on orgmode.org/worg. Essentially, start by creating your project directory (i.e. my-awesome-blog) and then two subdirectories: my-awesome-blog/org and my-awesome-blog/jekyll. The org directory is where the org-mode files will live, and the jekyll directory will be the standard structure of a normal Jekyll project.

my-awesome-blog
├── jekyll
└── org

Jekyll Theme

Next, get a Jekyll theme. Most Jekyll tutorials will tell you to now install Jekyll using bundler and run bundle exec jekyll new, but this setup doesn’t give very good control over the theme, so if you’re a customzation/ricing nut like me, you should clone a theme’s repository into the jekyll subdirectory

This page on the official Jekyll website has a bunch of links to themes. I have chosen minima v3 which is the development version of Jekyll’s flagship theme, minima. Whatever theme you choose, you should be able to get it running by cloning the theme’s source into your jekyll subdirectory.

Build and Test the Website Locally

At this point, you can cd into the jekyll subdirectory and run bundle install. This will install Jekyll along with all the necessary Ruby dependencies for the theme to work

To test the site locally, run bundle exec jekyll serve and navigate to http://localhost:4000. If everything worked properly, you should be able to see the Jekyll project running in test mode!

Cleanup and Intro Config

At this point, you may want to clean up some of the theme’s default files (especially from the _posts directory) and edit the _config.yml file to your liking.

Emacs Setup

Setup org-publish-project-alist

Following the worg guide again, we need to start by setting the org-publish-project-alist variable so that we are able to publish org files from the org directory to html files in the jekyll directory. This is necessary because Jekyll can only natively handle html and markdown, not org. I like to do this by creating a .dir-locals.el file in the project root and setting it to the appropriate values. This is what mine looks like:

((nil . (
  (org-publish-project-alist .
   (("org-librephoenix.com"
          ;; Path to your org files.
          :base-directory "~/Projects/librephoenix.com/org/"
          :base-extension "org"

          ;; Path to your Jekyll project.
          :publishing-directory "~/Projects/librephoenix.com/jekyll/"
          :recursive t
          :publishing-function org-html-publish-to-html
          :headline-levels 4
          :html-extension "html"
          :body-only t ;; Only export section between <body> </body>
    )

    ("assets-librephoenix.com"
          :base-directory "~/Projects/librephoenix.com/org/"
          :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|php"
          :publishing-directory "~/Projects/librephoenix.com/jekyll/"
          :recursive t
          :publishing-function org-publish-attachment)

    ("librephoenix.com" :components ("org-librephoenix.com" "assets-librephoenix.com"))))
  )
))

You essentially just need to publish two components:

  • org files in org directory -> html files in jekyll directory
  • asset files referenced in org directory by org files -> asset files referenced in jekyll directory by html files

The directories we’re concerned with are like this:

my-awesome-blog
 ├── jekyll
 │  ├── _posts
 │  └── assets
 └── org
    ├── _posts
    └── assets

To publish your org files into the Jekyll blog, run org-publish and select the appropriate project (in my case it is just librephoenix.com).

Structuring a Site Page in Org

You can setup basic pages in your org directory by creating org files such as:

  • index.org
  • about.org
  • blog.org
  • contact.org

These files will be exported as html files to the jekyll directory, like this:

 my-awesome-blog
 ├── jekyll
 │  ├── about.html
 │  ├── blog.html
 │  ├── contact.html
 │  └── index.html
 └── org
    ├── about.org
    ├── blog.org
    ├── contact.org
    └── index.org

Every org document should start with something like this:

#+title: My Page Title
#+options: toc:nil
#+begin_export: html
---
layout: base
title: My Page Title
---
Start of this awesome page...
#+end_src

The beginning html export block is necessary as yaml front matter, which tells Jekyll how to format the page. The layout property can be any template found in the _layouts directory.

Some themes have different default layouts, but for minima v3, the default layouts include: base, home, page, and post. The post theme is obviously for blog posts, and the page theme can be used to create normal pages, but the home layout is actually used to display a feed of blog post entries! I would encourage you to experiment with the different layouts available to the theme, and create new layouts (in the _layouts directory) if you find it necessary.

Don’t forget to run M-x org-publish!

Structuring Blog Posts in Org

Jekyll requires that blog posts have the format yyyy-mm-dd-post-title.html and that they must be in the _posts directory. To accommodate this, you must make the _posts directory inside of the org directory. It works similarly for the assets directory (for site assets like images) The directories we’re concerned with are like this:

my-awesome-blog
 ├── jekyll
 │  ├── _posts
 │  └── assets
 └── org
    ├── _posts
    └── assets

Furthermore, since posts have more metadata than normal pages, the front matter of posts can get a little fancier, like so:

#+title: My Post Title
#+options: toc:nil num:nil
#+begin_export: html
---
layout: post
title: My Post Title
excerpt: My excerpt
tags: cool example great
permalink: my-post-tite
---
#+end_export
Start of this awesome post...

Since there is so much going on here, I wrote a short function in elisp to automate the process of making a new post:

(defun org-jekyll-new-post ()
  (interactive)
  (setq new-blog-post-title (read-from-minibuffer "Post name: "))
  (setq new-blog-post-slug (downcase (replace-regexp-in-string "[^[:alpha:][:digit:]_-]" "" (string-replace " " "-" new-blog-post-title))))
  (setq new-blog-post-file (concat (projectile-project-root) "org/_posts/" (format-time-string "%Y-%m-%d") "-" new-blog-post-slug ".org"))
  (let ((org-capture-templates
        `(("p" "New Jekyll blog post" plain (file new-blog-post-file)
           ,(concat "#+title: " new-blog-post-title "\n#+options: toc:nil num:nil\n#+begin_export: html\n---\nlayout: post\ntitle: " new-blog-post-title "\nexcerpt: %?\ntags: \npermalink: " new-blog-post-slug "\n---\n#+end_export\n")))
   )) (org-capture)))

It essentially retrieves the post title from the minibuffer, and then with the magic of org-capture and org-capture templates, creates the template with the proper titles and dates into the org/_posts directory of the current project (via projectile).

This won’t work out of the box if you don’t have the project managed by git and added as a known project in projectile (M-x projectile-add-known-project).

GitLab Pages Setup

Navigate to GitLab and create a new project called YOURUSERNAME.gitlab.io. Start by pushing your local repo to this repo.

Next, we need to configure GitLab CI/CD using the .gitlab-ci.yml file in the project root. The default template for Jekyll on GitLab pages is something like this:

# The Docker image that will be used to build your app
image: ruby:latest
# Functions that should be executed before the build script is run
before_script:
  - gem install bundler;
  - bundle install;
pages:
  script:
    bundle exec jekyll build -d public;
  artifacts:
    paths:
      # The folder that contains the files to be exposed at the Page URL
      - public
  rules:
    # This ensures that only pushes to the default branch will trigger
    # a pages deploy
    - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH

This default configuration assumes that the actual Jeyll project is in the project root, but in this setup, it is in the subdirectory ./jekyll. The CI/CD config must be altered to accommodate this. I have mine like this:

# The Docker image that will be used to build your app
image: ruby:latest
# Functions that should be executed before the build script is run
before_script:
  - gem install bundler;
  - pushd jekyll && bundle install; popd;
pages:
  script:
    - pushd jekyll && bundle exec jekyll build -d ../public; popd;
  artifacts:
    paths:
      # The folder that contains the files to be exposed at the Page URL
      - public
  rules:
    # This ensures that only pushes to the default branch will trigger
    # a pages deploy
    - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH

Once these changes are pushed to GitLab, GitLab will automatically begin building your static site. You can monitor the build under Build -> Jobs and you can view the static site by navigating to Deploy -> Pages.

Note: In order for the website to be publically accessible, the repository must be publically accessible as well.

GitLab Pages Custom Domain

If you’d like to add a custom domain for your static site, navigate to Deploy -> Pages on your GitLab project and click New Domain.

You can follow the instructions from there and configure the DNS settings for your custom domain yourself via your chosen domain name registrar.

Just a few more things

Custom Styles

Feel free to experiment in customizing the css and scss files to style the page to your liking! Different themes may have slightly different structures, but if you know any CSS, you should be fine. If not, go read about CSS on w3schools.

Custom Includes

Many Jekyll themes (such as minima v3) have an _includes directory, which contain templated html elements (like the header, footer, etc..). I had fun customizing these to my liking, and this is very powerful since those customizations will persist among all pages!

This Website’s Source Code

Feel free to browse this website’s source code if you’re looking for some inspiration or more examples! Happy hacking!

Donation Links

If you have found my work to be helpful, please consider donating via one of the following links. Thank you very much!