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!
Table of Contents
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
inorg
directory ->html files
injekyll
directoryasset files
referenced inorg directory
by org files ->asset files
referenced injekyll
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!