Here are the steps you can follow to generate myblog

gem install jekyll
jekyll new myblog
cd myblog
git init .
git add .
git commit -m "Initial jekyll new myblog"

Live reload is now built in (no need for guard-livereload). You just need to run with option jekyll serve --livereload

For livereload you need to install Chrome plugin enable it and activate by clicking on icon when you open a page. There is also plugin for firefox You can use javascript version.

Custom domain

If your repo is the only or is primary, you can use url you just need to rename repository to that name.

Do not need to rename if you want custom domain, you can enable it from settings or you can just create CNAME file manually since that is the same

git chechout -t origin/gh-pages
echo "" > CNAME

You need to configure your domain name provider to point to your github respository with CNAME record to point to {username}, for example CNAME Check when it is updated, usually in one hour

# note that second column is TTL time to live in secods
dig +nostats +nocomments +nocmd
;			IN	A		3600	IN	CNAME	3600	IN	A	3600	IN	A

Use this only for subdomains (, and not for apex domain. Adding CNAME record to the root of your domain will disable MX and TXT records. If you need to apex domain (and not to destroy other records) you can try with ANAME record (not supported in loopia), or use Cloudflare flattening

Default is serverd under HTTPS, but custom domain is serverd under HTTP. To enable HTTPS for custom domains you can try cloudflare but recently, all github pages are enypted with free certificate.


Adding CNAME on root domain is possible, it is calling CNAME Flattening. You can use cloudflare and heroku to catch all subdomains to same herokuapp Add * (same as it is normal subdomain) to heroku, and use that CNAME value on for cloudflare.

Note that only paid plans will use Cloudflare CDN… this subdomains will go directly to herokuapp. For example if you add root and www and wildcard subdomain you will see that root and www goes to Cloudflare IP addresses.

dig +noall +answer		300	IN	A		300	IN	A

dig +noall +answer	300	IN	A	300	IN	A
dig +noall +answer	300	IN	CNAME	60 IN A	60 IN A


dig  +nostats +nocomments +nocmd
;		IN	A	3405	IN	CNAME 47 IN	A


If you want redirection from non www to www, than use Forwatd -> Url redirect (301 or 302) but uncheck “Synchronize the domain and subdomain www” loopia dns

Jekyll on github

Easiest way to start a blog on github it to fork and rename it to and start editing using There is nothing special there, just simple jekyll project, with _includes for discuss, analytics, meta and uses gems: jekyll-sitemap which github supports. It does not use theme. It is very similar to default jekyll theme minima

Jekyll default template minima can be overriden with

bundle show minima
# ~/.rvm/gems/ruby-2.5.1/gems/minima-2.5.0
mkdir _includes
cp `bundle show minima`/_includes/@(footer|header|head).html _includes/
git add .
git commit -am 'cp `bundle show minima`/_includes/@(footer|header|head).html _includes/'


This gist explain what to do. In additional you need to use bundle install && bundle exec jekyll build -d out as build command, use proper ruby supported by travis rvm: - 2.2, exclude vendor from jekyll, and note the label string that travis cli generate when you encrypt.

You need to generate keys which Travis will use to deploy. You need to write to deploy_key and to not label from travis cli.

ssh-keygen -t rsa -b 4096 -C "[email protected]" deploy_key
travis encrypt-file deploy_key
git add deploy_key.enc
mv deploy_key ~/.ssh/
cat >> << HERE_DOC
set -e # Exit with nonzero exit code if anything fails


function doCompile {
  bundle install
  bundle exec jekyll build -d out

# Pull requests and commits to other branches shouldn't try to deploy, just build to verify
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]; then
    echo "Skipping deploy; just doing a build."
    exit 0

# Save some useful information
REPO=`git config remote.origin.url`
SSH_REPO=${REPO/https:\/\/\//[email protected]:}
SHA=`git rev-parse --verify HEAD`

# Clone the existing gh-pages for this repo into out/
# Create a new empty branch if gh-pages doesn't exist yet (should only happen on first deply)
git clone $REPO out
cd out
git checkout $TARGET_BRANCH || git checkout --orphan $TARGET_BRANCH
cd ..

# Clean out existing contents
rm -rf out/**/* || exit 0

# Run our compile script

# Now let's go have some fun with the cloned repo
cd out
git config "Travis CI"

# If there are no changes to the compiled out (e.g. this is a README update) then just bail.
if [ -z `git diff --exit-code` ]; then
    echo "No changes to the output on this push; exiting."
    exit 0

# Commit the "changes", i.e. the new version.
# The delta will show diffs between new and old versions.
git add .
git commit -m "Deploy to GitHub Pages: ${SHA}"

# Get the deploy key by using Travis's stored variables to decrypt deploy_key.enc
openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in deploy_key.enc -out deploy_key -d
chmod 600 deploy_key
eval `ssh-agent -s`
ssh-add deploy_key

# Now that we're all set up, we can push.
cat >> .travis.yml << HERE_DOC
language: ruby
script: bash ./
  - 2.2
  - ENCRYPTION_LABEL: "find-label-in-output"
  - COMMIT_AUTHOR_EMAIL: "[email protected]"
cat >> _config.yml << HERE_DOC
  - Gemfile
  - Gemfile.lock
  - vendor
git add .travis.yml _config.yml deploy_key.enc
git commit -m "Adding travis deploy scripts"

Very usefull one line command when debugging travis

git add . && git commit --amend --no-edit && git push -f

You can edit online with Make sure you are editing master branch. Make sure github setting default branch is master branch.

You can add prose related config. You can exclude files with ignore: . You can add custom fields for some front matter with metadata: _posts: .... First key is folder name not the ruby class so if you have page or post inside folder, than use folder name instead of _posts. Title is updated in header so do not need to define title metadata. Use "" for all files. Nice example of prose.


There is heroku buildpack than can build jekyll for you and serve static site so you can use any plugins.

Serve under subfolder

Since github pages serve your project under subfolder you need to use special config variable baseurl. Add to your _config.yml a line baseurl: /blog. This is not needed if you use custom domain like

To properly serve your assets and link your pages you need to prepend all links with site.baseurl. For example in markdown ![My picture]({ { site.baseurl }}/assets/my_picture.png) or <link rel="stylesheet" href="{ { site.baseurl }}/assets/css/main.css"> or <a class="post-link" href="{ { post.url | prepend: site.baseurl }}">{ { post.title }}</a>

Development environment

If you need to separate production from development (for example analytics), you can use liquid variable jekyll.environment which can be exported or used inline with JEKYLL_ENV=development jekyll serve --watch

<!-- index.html -->
    {% if jekyll.environment == "development" %}
      Hello admin
    {% endif %}

Automatic deploy to gh-pages using rake

Rake tasks are perfect for deploying since you need just to type rake and it will be live (if you set up properly). This is my Rakefile that builds my blog (stored on bitbucket) and push to github. This is nice since I do not want to share history (all commits from the beggining). On github, you can find only one commit “Site updated at …”

git remote add origin [email protected]:duleorlovic/
git remote add github [email protected]:duleorlovic/
# Rakefile
# You need to set up git remote to github, for example:
# git remote add github [email protected]:duleorlovic/blog.git
# Require jekyll to compile the site.
require "jekyll"
require 'tmpdir'

task :default => "blog:publish"
# Github pages publishing.
namespace :blog do
  # Because we are using 3rd party plugins for jekyll to manage the asset pipeline
  # and suchlike we are unable to just branch the code, we have to process the site
  # localy before pushing it to the branch to publish.
  # We built this little rake task to help make that a little bit eaiser.

  # Usaage:
  # bundle exec rake blog:publish
  desc "Publish blog to gh-pages"
  task :publish do
    # Compile the Jekyll site using the config.{
      "source"      => ".",
      "destination" => "_site",
      "config" => "_config.yml"

    # Get the origin to which we are going to push the site.
    origin = `git config --get remote.github.url`

    # Make a temporary directory for the build before production release.
    # This will be torn down once the task is complete.
    Dir.mktmpdir do |tmp|
      # Copy accross our compiled _site directory.
      cp_r "_site/.", tmp

      # Switch in to the tmp dir.
      Dir.chdir tmp

      # Prepare all the content in the repo for deployment.
      system "git init" # Init the repo.
      system "git add . && git commit -m 'Site updated at #{}'" # Add and commit all the files.

      # Add the origin remote for the parent repo to the tmp folder.
      system "git remote add origin #{origin}"

      # Push the files to the gh-pages branch, forcing an overwrite.
      system "git push origin master:refs/heads/gh-pages --force"

    # Done.

Adding Table of Content

For README you can run command that extract all headers that you can copy and paste to README. Alternativelly you can insert

## Table of Contents

and run

gh-md-toc --insert

If you need automatic Toc for a any jekyll page than use dafi jekyll-toc-generator.

I modify to use original id-s (that is hypernated version of header content) in duleorlovic/jekyll-toc-generator

You can put defaults to not have a Toc, and enable on post that you want Toc to show.

# _config.yml
      path: ""
      type: "posts"
      noToc: true

Adding sitemap

echo "gem 'jekyll-sitemap'" >> Gemfile
echo "url:
  - jekyll-sitemap
" >> _config.yml

Sitemap will be automatically generated with jekyll serve. Add sitemap: false for pages that you don’t want to appear in sitemap (like google site verification).

Jekyll variables and categories

Jekyll contains variables: site, layout, content, paginator and page.

{ { site }} is Jekyll::Drops::SiteDrop class and contains

  • site.posts array of all posts objects Jekyll::Document collection=post they are descending order. To access most recent you can use loop { % for post in site.posts limit: 3 %} or assign ``
  • site.pages array of all pages Jekyll::Page

{ { page }} is an object with

  • page.content preproccessed content
  • like
  • page.path like example/
  • page.url like /example/contact.html

  • all front matters variables like page.layout, page.title

if current page is post we have additional properties

  • page.title
  • page.excerpt it is first paragraph. if you need to limit you can use Metaprogramming Ruby 2 Paolo Perrotta . You can use specific delimiter on a post in frontmatter excerpt_separator: <!--more--> or in config (but than default “paragraph” excerpt will not be used, and you need to use separator on every post)
  • page.categories
  • page.tags
  • page.previous

You can put your posts inside folder, for examle kayak/canoe/_posts. This means that posts will have ['kayak', 'canoe'] categories. You can add more categories using front matter categories: sprint.

Since pages do not have category, you cat mark them manually or check path, like this

{ % capture page_category = page.path | split: '/' | first %}

To list similar posts you can use this snippet in _layout/default.html

<style type='text/css'>
  .similar-links {
    display: inline-block;
    padding: 10px;
{% for cat in page.categories %}
  {% if site.categories.[cat].size != 1  %}
    <ul class="similar-links">
      <li>Look similar <b> {{ cat }}</b> tag:</li>
      {% for p in site.categories.[cat] %}
        {% unless p.url == page.url  %}
            <a href="{{ p.url }}">{{ p.title }}</a>
        {% endunless %}
      {% endfor %}
  {% endif  %}
{% endfor %}


Just copy and paste search.json and other snippets from Simple-Jekyll-Search


For gh-pages, you just need 404.html at root for server to use it when it does not find the page.


You can serve using different port, just add port: 4001 to your _config.yml file.


If you want to create theme you can follow this steps:

jekyll new-theme jekyll-theme-booster
vi jekyll-theme-booster.gemspec
# edit description and url
gem build jekyll-theme-booster.gemspec
gem install jekyll-theme-booster-0.1.0.gem

Running example site

You can put posts inside root folder but than you do not know which file belongs to theme and which is just example. So better is to use separated folder and use rake example to run that site.

# Rakefile
# based on
require "bundler/gem_tasks"
require "jekyll"
require "listen"

def listen_ignore_paths(base, options)

def listen_handler(base, options)
  site =
  proc do |modified, added, removed|
    t =
    c = modified + added + removed
    n = c.length
    relative_paths ={ |p| }
    print Jekyll.logger.message("Regenerating:", "#{relative_paths.join(", ")} changed... ")
      puts "regenerated in #{ - t} seconds."
    rescue => e
      puts "error:"
      Jekyll.logger.warn "Error:", e.message
      Jekyll.logger.warn "Error:", "Run jekyll build --trace for more information."

task :example do
  base ='.').expand_path
  options = {
    "source"        => base.join('example').to_s,
    "destination"   => base.join('example/_site').to_s,
    "force_polling" => false,
    "serving"       => true,
    "theme"         => "jekyll-theme-booster"

  options = Jekyll.configuration(options)

  listener =
    :ignore => listen_ignore_paths(base, options),
    :force_polling => options['force_polling'],
    &(listen_handler(base, options))

    listener.start "Auto-regeneration:", "enabled for '#{options["source"]}'"

    unless options['serving']
      trap("INT") do
        puts "     Halting auto-regeneration."
        exit 0

      loop { sleep 1000 }
  rescue ThreadError
    # You pressed Ctrl-C, oh my!


Editing theme

Only files that are in repository are used for gem. gemspec uses git ls-files and default gemspec is to inlude assets, _includes, _layouts and _sass. You can see what is in gem with gem open jekyll-theme-booster.

Some files will not be used: _config.yml.

Using theme

When you edit template, you need to build, install and restart your jekyll serve proccess to load updated gem.

It is enough to specify path as <link rel="stylesheet" href="assets/css/animate.css"> but for relative folder <link rel="stylesheet" href="{ { "assets/css/animate.css" | relative_url }}"> (that is needed for all navigational links).

It is advised to be in same ruby version rvm list and rvm gemset list.

Another theme example is minimal-mistakes-jekyll

Yet another theme


  • table can be generated with |header1|header2
  • url can be written like <> (http:// at the beggining is mandatory) instead of writting it twice [](
  • internal link to post is with {% %} like [My page]({% link 2016-03-02-my-page.markdown %})
  • images ![alt text]({ { site.baseurl }}/assets/path_to_image "Title text") If you want to use image in than you need to store image and use
    ![trk-datatables](test/trk_datatables_with_daterangepicker.png "TRK Datatables")

    This will work on github but not if README is shown on other sites

  • strikethrough (words crossed, deleted, removed text) can be used with <s>Example</s> or <del>Example</del> (with kramdown no shortcut works like -- ~~)
  • for code you can use {% highlight javascript %}, {% highlight ruby %}, {% highlight bash %}
  • use inline html in markdown if you use span level tags <span>,<bold>… for block level tags <div> you need to add blank line.


  • inline and block html elements
    <div class='pull-right'>
      Text right
    {::options parse_block_html="true" /}
    Use *markdown* for span elements
  • definition list dl dt dd
    term inside dt
    : definition inside dd
  • block attributes are set with folloing IAL inline attribute list
    > A blockquote
    {: title="My title" .class1 #my-id}
  • share attributes using ALD atribute list definition
    {:refdef: .my-class}
    {: refdef}
  • span level elements using IAL inline attribute list
    This is *red*{: style='color: red'}.
  • extension using ::, like ::comment
    this is ignored completely

Liquid in markdown

  • to show jekyll mustache inline you can add space, like { % bla_tag %}. Note that in blocks of code you can use mustache, so this is only for inside tick.
  • to show {{ }} using markdown you need to escape them like {{ '{{ ' }} }} first closing brackets will close everything that comes after first opening brackets. All other closing brackets are simply rendered. This does not work if that line breaks in mutliple lines (probably string need + or something). To simplify I just put space between first { { }}
  • if you need to show ` than use backslash or put a space ` so it wont be applied`

Internal jekyll tags

link Note that you do not have to use quotes: ' or ".

  • { % post_url %}. Note that you can not use string filters like prepend: inside tags (works only with double curly brackets { { }}, so for link to another post (by it’s file name) when site.baseurl is set, you need [my-post]({ { site.baseurl }}{ % post_url 2015-12-20-my-post %})
  • when you want to link assets you can use { { "/assets/style.css" | relative_url }}
  • you can link to (link_to) pages also (not just posts) with { { site.baseurl }}{ % link news/index.html %}. Parameter for link should contain extension (for example .md).

Note that in liquid version 4 you can use concat filter but it is not supported in jekyll 3.4 (liquid 3.0.6) concat example. In old liquid you can use append but that is only for strings. You should point to latest jekyll from github which uses Liquid 4

# get latest jekyll which uses liquid 4
gem "jekyll", github: 'jekyll/jekyll'

Adding tag, filter

All plugins will be loaded from _plugins folder. Githib ignore this folder.

# _plugins/alert_tag.rb
module Jekyll
  module AlertFilter
    def alert(text)
      unescaped = if text.class == String
                  CGI.unescapeHTML text

This is used to alert text { % alert this is text %}

# _plugins/alert_filter.rb
module Jekyll
  module AlertFilter
    def alert(text)
      unescaped = if text.class == String
                  CGI.unescapeHTML text

Filter is used like { { page | alert }}


For adding scss support, you need to put your main scss file inside for example assets/main.scss and it needs to have double three lines ---. From there you can use @import "file_from_scss"; to load partials from _sass folder

# this is assets/main.sass which imports other sass files
# NOTE that you can not import css file, it needs to be sass/scss
# import "file.css" # this will not be imported at compile time, but in browser
# import "file" # this will search for file.sass and import at compile time

@import "overrides";
<!-- override default template file _includes/head.html -->
  <link rel="stylesheet" href="/assets/main.css">

Coffee script

To enable coffee script you need to add to config.yml

cat >> _config.yml << HERE_DOC
  - jekyll-coffeescript

And you coffee files need to have three lines --- for example

# assets/js/
class Cart
  constructor: (@buttons, @cart) -> (event) =>
      @textContent = 'Added'
      do event.preventDefault
      itemContainer =
      itemNumber = do $ itemContainer
      .find 'h3.panel-title'

      listItem = do $ @cart
      .find 'li:last'

      listItem.text itemNumber
      @cart.append listItem
      .fadeIn 'slow'

      console.log itemContainer

$ ->
  cart = new Cart $('form button'), $('ul#cart')

and they will be compiled, so you can include them <script src="assets/js/cart.js"></script>



  • if you want to move page it is advised to leave old page some time so you do not confuse crawlers. You can add redirection in html
<!-- narucivanje.html -->
    <meta http-equiv="refresh" content="0; url="/>
    You are redirected to


Google io slides can be found After you clone the repository


  • date 2020
  • add bootstrap
    yarn init
    yarn add bootstrap jquery popper.js
    # _config.yml
        - _sass
        - node_module
    # css/main.sass
  • debug slow rendering with
    jekyll s --profile --verbose --trace

    To render only last post

    jekyll s --watch --limit_posts 1

    Rebuild only that is changed

    jekyll s --incremental
  • permalinks only support date and categories. To use author in permalink you need to use plugin
  • seo tags and Open Graph meta name tags are added using

  • jekyll with webpack To add boootstrap, run npm add bootstrap jquery popper.js and add

    // _src/index.js
    import 'jquery';
    import 'popper.js';
    import 'bootstrap';
    // _src/index.scss
    @import '~bootstrap/scss/bootstrap';

    For links you need to use relative url <a href="/2020/04/07/metaprogramming-ruby/">Metaprogramming Ruby</a>

    I disabled minify_html so it runs faster on development

    To rebuild you need to stop and start again npm start