Ondrej Famera - top logo

Why and how to build your personal website with Jekyll

As some projects come and go I got to situation where I needed to replace the old unmaintained Bloxsom script for generating this website with something comparably simple but still maintained. The system should be reasonable easy to install on some of the currently supported Linux distribution and allow me to use my layout and theme that I have used on site before.Doing a little search and try exercise I have ended with project Jekyll that had fit the needed requirements and seemed to be reasonably installable and customizable.

What is the Jekyll and how it typically operates

Jekyll is a simple, extendable, static site generator. You give it text written in your favorite markup language and it churns through layouts to create a static website. Throughout that process you can tweak how you want the site URLs to look, what data gets displayed in the layout, and more. - Jekyll Quickstart

In typical operation you create the content in your favourite editor and save it into files in HTML or markdown syntax. These pages are allowed to contain some variables provided and later expanded by Jekyll. Jekyll will then either start in the "serve" mode where it will run a simple webserver with the page based on the files that you have created. Alternatively the Jekyll will be run in "build" mode where it just puts static data together and output the whole structure of web in directory _site/ that can be then copied to some real webserver to be served. In both cases the Jekyll doesn't need to be present on the target production machine. All the configuration that is needed for the webpage to be generated by Jekyll can be stored in version control system such as Git easily.

Preparing the system that will run Jekyll

As for the distribution of choice I have randomly chosen Fedora 29 as one of the distributions that I use to do testing of various things. The official installation step for "Other Linux distros" recommends to install whole @development-tools of packages that seems to be me a bit excessive. I have used following set of packages as the minimum that works for me.

# dnf install rubygem-jekyll ruby-devel libffi-devel redhat-rpm-config gcc make
...
===============================================================================
Install  75 Packages

Total download size: 53 M
Installed size: 158 M
...

Additionally to just installing the packages I have allowed the access to HTTP port (80/tcp) in firewalld so Jekyll simple webserver can be accessed for preview of generated webpage.

# firewall-cmd --add-service=http --permanent
# firewall-cmd --add-service=http

Creating new site and running jekyll server

To create a new empty sample webpage with Jekyll we can run command below that will create directory with same name as last parameter. Note: If you have already created Jekyll webpage on different system, then skip to chapter XXX later in this post.

# jekyll new my_sample_page
New jekyll site installed in /root/my_sample_page.

Above command has created the following files and directories

my_sample_page/
├── 404.html       # sample 404 - not found webpage
├── about.md       # sample "About" page
├── _config.yml    # Jekyll configuration file
├── Gemfile        # Jekyll Gemfile
├── index.md       # sample "Index" page
└── _posts         # directory with blog posts
    └── 2019-03-27-welcome-to-jekyll.markdown  # sample blog post

To have a look on what the sample page with above structure looks like lets change into directory where we have created a sample page and run the command below to start Jekyll simple webserver. Webserver will show our sample page on any IP address of the machine and on standard HTTP port (80/tcp).

# cd /root/my_sample_page
# jekyll serve --host=0.0.0.0 --port=80

To access the web page servered by above command you need to visit http://XXX.XXX.XXX.XXX/ where XXX.XXX.XXX.XXX is IP address of the server on which the command is run. Argument --host=0.0.0.0 tells Jekyll webserver to listen on all IP addresses instead of default 127.0.0.1. Argument --port=80 will instruct Jekyll to run webserver on TCP port 80 that is a standard HTTP port.

Minimizing the sample page

While the sample page provides a balanced overview on what Jekyll can do out of box it hides some parts that are enabled in example by default. This includes the use of default theme named 'minima' and use of plugin that generates the feed.xml on page. Following steps would remove the use of the default theme and reduce the example to minimum in which only own template and files are used. This can serve a good starting point for using own design and it is something that even this webpage came through when it was migrated to Jekyll. To see list of variables and their default values you can check the Default Configuration.

Minimizing the _config.yml

For simple use we can minimize the configuration file to following two lines.

title: My page
baseurl: "" # the subpath of your site, e.g. /blog

Above leaves us with only 2 variables:

  • baseurl - this describes the start of URL where the webpage will be located
  • title - Stores the title of page that can appear in address bar that we will use later - realistically even this can be omitted

Minimizing the Gemfile

While in the _config.yml we have disabled some plugins and options that will not be later used by Jelyll, the second file that needs to be updated to reflect the changes is the Gemfile. To continue with our minimalism this file can be reduced in this example to following 2 lines.

source "https://rubygems.org"
gem "jekyll", "~> 3.8.5"

Lines that we have removed are used either on different platform (performance optimisation on Windows) or are related to components/plugins that we will not use (default theme 'minima' and 'jekyll-feed' plugin). These lines were removed as we won't need them.

Testing the minimized version page

Lets try to check out how the minimized version of page would look like in the browser with command that we have used earlier.

# cd /root/my_sample_page
# jekyll serve --host=0.0.0.0 --port=80
...
     Build Warning: Layout 'post' requested in _posts/2019-03-26-welcome-to-jekyll.markdown does not exist.
     Build Warning: Layout 'default' requested in 404.html does not exist.
     Build Warning: Layout 'page' requested in about.md does not exist.
     Build Warning: Layout 'home' requested in index.md does not exist.
...

Trying this out will lead to warnings above and empty page, so what went wrong? We have removed the default theme that provided templates/layouts to pages that Jekyll generates. Without the layout files it cannot generate the page as that file describes how to generate the page. Lets have a look on how to create these layouts.

While we can use separate layouts for each page, the idea of layouts is that we can create a "frame" into which we will just put the content of the webpage and Jekyll will generate pages based on layout and the content we supply. The content that we provide is currently in pages like index.md,about.md, 404.html and _posts/2019-03-27-welcome-to-jekyll.markdown. To create a layout for this content lets create directory _layouts/ and create a file home.html in it containing the below code.

# mkdir /root/my_sample_page/_layouts/
# vim /root/my_sample_page/_layouts/home.html
<!doctype html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <title>{{ site.title }}</title>
</head>
<body>
<h1>This is the main page</h1>
        {{ content }}
        <div class="footer bar">
                <!-- Footer -->
                © 2019, backend by <a href="https://jekyllrb.com/">Jekyll</a>
        </div>
</body>
</html>

In above text note the two things that doesn't look like HTML code and that has a special meaning - {{ site.title }} and {{ content }}. These two things are variables that Jekyll will populate when generating the page. First one, {{ site.title }}, will take value of variable title from _config.yml file. Second, {{ content }}, will take all text from file defining the page after initial header (Front Matter). Combining these two elements you can see how the layout can be used as template for many pages that should have same ... layout.

In additional to this single layout that will be represented on main "home" page lets create a second layout file named page.html that will be used on all other pages.

# vim /root/my_sample_page/_layouts/page.html
<!doctype html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <title>{{ page.title }}</title>
</head>
<body>
        {{ content }}
        <div class="footer bar">
                <!-- Footer -->
                © 2019, backend by <a href="https://jekyllrb.com/">Jekyll</a>
        </div>
</body>
</html>

While the file looks very similar to previous one, it uses different variable for page title - {{ page.title }}. This variable takes the value from the file with content rather than from _config.yml. After this lets try to start the Jekyll webserver and observe if we can see initial page and if we are getting any other warnings.

# cd /root/my_sample_page
# jekyll serve --host=0.0.0.0 --port=80
...
     Build Warning: Layout 'post' requested in _posts/2019-03-26-welcome-to-jekyll.markdown does not exist.
     Build Warning: Layout 'default' requested in 404.html does not exist.
...

At this point we would see initial page and the number of warning will be reduced. The remaining warning are coming from files _posts/2019-03-26-welcome-to-jekyll.markdown and 404.html. If we adjust the layout: variable in them to page these errors should get away. Adjusting these files can be done even while the jekyll server is running and we will see that it will automatically detect the changes when we write them to the files. Note that changes in _config.yml requires the manual stop with ctrl+c and start repeating the start to get these changes to be recognized.

Adding custom files/images to the page

At this point my setup was missing only one thing - custom files that should not be generated by Jekyll, but just served as they are. How to achieve this? Simply by putting the files inside of directory with other files. The one important thing to mention is on how to refer to these files in the content of the pages. Lets take for example some image file - lets use this one https://www.famera.cz/blog/assets/images/menu_linuxcz_200x40.gif - and add it as picture on the main page of our website. To do this we would need to first download the picture and save it inside of the directory structure where we have pages with other content. I will save this image in the /images/ directory. Then I will add the picture to main page with code below.

# mkdir /root/my_sample_page/images/
# curl https://www.famera.cz/blog/assets/images/menu_linuxcz_200x40.gif > /root/my_sample_page/images/menu_linuxcz_200x40.gif
# vim /root/my_sample_page/index.md
---
# Feel free to add content and custom Front Matter to this file.
# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults

layout: home
---
<img src="{{ site.baseurl }}/images/menu_linuxcz_200x40.gif" alt="linux.cz logo"/>

Reloading the index page I should now see the image that was added. Same thing as with images can be done with CSS files, Javascript or any other attachments that you wanna link in your page. Important to note is the use of variable {{ site.baseurl }} that will take value from _config.yml. In case that we later decide to change the base URL of the page and move it elsewhere, this will make our life much easier as we wouldn't have to think about updating the links like this.

Generating the static files that can be copied to real webserver

While jekyll serve is great choice for quick development of page and testing how the site looks, it is not something that can be recommend for use in real production. For production the real webserver such as Apache, Nginx or others should be used. But which files should we provide to it so it shows same webpages as we have seen in our testing? Glad that you have asked :) While the jekyll server transparently creates the needed pages in _site/ directory we can also instruct Jekyll to just generate these pages and don't provide us the webserver with following command.

# cd /root/my_sample_page
# jekyll build
Configuration file: /root/my_sample_page/_config.yml
            Source: /root/my_sample_page
       Destination: /root/my_sample_page/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
                    done in 0.161 seconds.
 Auto-regeneration: disabled. Use --watch to enable.

With above we can then easility take all the structure from directory _site and copy it to our real webserver.

Generating site that was created by Jekyll on other machine

In case that we want to we would like to build webpage or run the jekyll server on machine where this was not run before for our webpage, there is one extra step that needs to be done. The command below when run inside of the directory with our webpage will try to fetch and install any missing ruby gems that are needed for building our webpage.

# cd /root/my_sample_page
# jekyll build
Could not find public_suffix-3.0.3 in any of the sources
Run `bundle install` to install missing gems.
# bundle install
...
# jekyll build
Configuration file: /root/my_sample_page/_config.yml
            Source: /root/my_sample_page
       Destination: /root/my_sample_page/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
                    done in 0.163 seconds.
 Auto-regeneration: disabled. Use --watch to enable.

Note that in above example when we have attempted to run jekyll build command without preparation, it has failed with error about missing ruby gem that is required for building this webpage. Using the bundle install command we have installed the missing dependencies and after that retried to build the webpage. This time we have succeeded

Conclusion

After following the above text and examples you should be now able to setup the Jekyll on the Fedora 29 system and be able to simplify the sample webpage to more minimalistic version. Above steps are also at the same time the documentation for myself on what everything was needed to change to migrate generation of my webpage here to use Jekyll. At present time I'm still using the same CSS code and a bit modified HTML code that was not changed because of Jekyll, but that could be changed much easier thanks to it. I think that I can finally decommission the machine that was kept alive only to provide compatible dependencies with Blosxom that I have used before.

Last change .