STEP1. Install ruby and rvm(ruby version manager) and rubygems

Check the previous article: Install Ruby, rvm and rubygems. Notable facts is that ruby software is packaged in so called gems.

STEP2. Install Jekyll Static Site Generator

jekyll is itself a gem.

Install jekyll’s bundler package manager (is a gem). Note bundler is expected to run from project folder. Bundler is the equivalent of PHP composer for Ruby world. It writes a file called Gemfile. Gems are locked in a Gemfile.lock. Gems can be fetched from ‘https://rubygems.org’ or any other source specified from Gemfile with keyword source. Bundler is a much more powerful, complex and subtle tool than RubyGems. While gem can be used a system level and at project level, bundle It will replace gem at project level. Install them both at once

gem install jekyll bundler
jekyll -v

STEP4. Spawn the new blog

Create the new blog inside the container:

$ bundle exec jekyll new site

If you look into your project directory, you will see the following structure:

tree

├── 404.html
├── about.markdown
├── _config.yml
├── Gemfile
├── Gemfile.lock
├── index.markdown
└── _posts
    └── 2025-04-26-welcome-to-jekyll.markdown

STEP5. Setup the Docker environment

The goal of this article is to build a simple Jekyll blog without installing anything on your machine (using Docker technology) and automate the deployment to a free GitLab page using GitLab CI.

Preliminary step: install docker and docker-compose: If you don’t know what Docker is read my article Docker Presentation.

Installing docker is optional since you can run at anytime bundle exec jekyll serve. Docker will serve the purpose of easing the development.

Create a new file in your root project docker-compose.yml and then paste inside the following:


services:
  jekyll:
    image: jekyll/jekyll:latest
    command: jekyll serve --watch --force_polling --verbose --config _config.yml _config_dev.yml
    ports:
      - 4000:4000
    volumes:
      - .:/srv/jekyll

Set a different port 4001, 4002 etc for various projects to avoid conflicts when working simultaneoulsy to different projects.

Wake it up

docker-compose up -d

At this moment the command: jekyll serve --watch --force_polling --verbose --config _config.yml _config_dev.yml is being run. If it is necessary to run it manually as shown below.

Build the blog

Run $ docker-compose run jekyll jekyll bash to get inside the container. Everything you produce here will be mirrored outside in your file structure.

There is no index.html file yet. Run bundle exec jekyll build in your container prompt.

Now you see the structure has been enriched and a new folder named _site has emerged. Here is your site that is served locally by your mini server that bundle incorporates. This is the part that will be copied to your hosting server.

tree

├── 404.html
├── about.markdown
├── _config.yml
├── Gemfile
├── Gemfile.lock
├── index.markdown
├── _posts
│ └── 2025-04-26-welcome-to-jekyll.markdown
└── _site
    ├── 404.html
    ├── about
    │ └── index.html
    ├── assets
    │ ├── main.css
    │ ├── main.css.map
    │ └── minima-social-icons.svg
    ├── feed.xml
    ├── index.html
    └── jekyll
        └── update
            └── 2025
                └── 04
                    └── 26
                        └── welcome-to-jekyll.html

Building and previewing the blog

When everything is in place we can build and serve our blog while watching for changes:

There are may ways you can launch the preview of your site:

bundle exec jekyll serve --watch --force_polling
$ docker-compose run --service-ports jekyll jekyll serve --source=site

If you browse to http://localhost:4000, you will see your new blog!

In a more complex setting you may run

bundle exec jekyll serve --watch --force_polling --verbose --config _config.yml _config_dev.yml --port 4001

I use most often

bundle exec jekyll serve --livereload --trace

Gitlab setup

You need an account on gitlab.com. It’s free.

Add a .gitlab-ci.yml file at the root of the project, containing the following:

pages:
  image: alpine:latest
  script:
  - cp -R ./dist ./public
  artifacts:
    paths:
    - public
  only:
  - master

From now, a GitLab CI pipeline will be launched each time there is a push to the master branch and will mount our dist folder content as the root of the GitLab page.

Setup a new Gitlab repository.

Commit and push the changes to the master branch:

$ git init
$ git remote add origin git@gitlab.com:{username}/project.git
$ git add .
$ git commit -m "Initial commit"
$ git push -u origin master

A new pipeline has just been created. Once it’s passed, the blog is available at https://{username}.gitlab.io/project/.

But, as you can see, asset paths are broken because the base url of the page is /project/.

In order to change it, we need to edit /src/_config.yml:

baseurl: "/project"

We can now rebuild the site:

$ docker-compose run jekyll jekyll build --source=site

When our changes are committed and pushed to the repo, the paths are fixed.

Create a Jekyll Based Blog

Clone a jekyll repository and rename it to projectid.gitlab.io

Create .gitlab-ci.yml

I use image ruby 3.1 to make sure is consistent with the server version.

image: ruby:3.1

variables:
  JEKYLL_ENV: production

before_script:
  - bundle install

test:
  stage: test
  script:
  - bundle exec jekyll build -d test
  artifacts:
    paths:
    - test
  except:
  - master

pages:
  stage: deploy
  script:
  - bundle exec jekyll build -d public
  artifacts:
    paths:
    - public
  only:
  - master

Daily Workflow

When we want to work on the blog, for example, to create a new post, we just need to start Jekyll to build and serve the site:

$ docker-compose run --service-ports jekyll jekyll serve --source=site
The site will be automatically reloaded when there is a change in the src folder 

To preview it, we can browse to http://localhost:4000/project/.

When we are happy with the changes, we can push them to the GitLab repo and the live blog will be rebuilt.

bundle exec jekyll serve –livereload –trace

Safe Backup of Your Work

Now let’s push our repository to gitlab. We assume you create that repo in Gitlab. All the instructions are in the README file once you create the repo.

git remote -v
gitlab  git@gitlab.com:jazio/jazio.eu.git (fetch)
gitlab  git@gitlab.com:jazio/jazio.eu.git (push)
origin  git@steve:~/jazio.eu.git (fetch)
origin  git@steve:~/jazio.eu.git (push)
git push gitlab