If you to need to host a blog, GitLab can help to build and host it with GitLab Pages. In this tutorial we'll use GitLab CI and Jekyll to deploy your blog. This is the configuration I use for my personal website.
Prerequisites
Have a project hosted on GitLab
1. Create and build your blog locally
1.1 Create a homepage and an article
cd my-project
In index.html
---
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My first blog</title>
</head>
<body>
<section>
<h2>
<!-- {{ link | absolute_url}} is a 'Liquid' expression Jekyll will interpret to prefix the link with the url and the base_url previously defined -->
<a href="{{'/articles/my-first-article.html' | absolute_url}}">My first article</a>
</h2>
</section>
</body>
</html>
The 2 dashed lines at the top of the markdown file define your Front Matter, this is a
.ymlconfiguration section for your page we will talk about later
In articles/my-first-article.html
---
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My first article</title>
</head>
<body>
<p>Hi Mom</p>
</body>
</html>
In Gemfile
source "https://rubygems.org"
gem "jekyll"
In _config.yml
baseurl: /<name_of_your_gitlab_project>
1.2 Test locally with Jekyll
So you do not have to run the GitLab pipeline to ensure your blog's code does not have bugs.
This tutorial has been tested on Ubuntu 24.04 (some commands in this section may not be adapted to your OS)
Useful links:
Install ruby (via rbenv) and Jekyll
# Install dependencies to run ruby
sudo apt update && sudo apt install -y git curl build-essential libz-dev libffi-dev libssl-dev libyaml-dev
# Clone rbenv and install it
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
~/.rbenv/bin/rbenv init
source ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
Then find the latest stable release of ruby with
rbenv install -l
We will remember it as RUBY_VERSION, replace RUBY_VERSION in the following commands with the version you have found.
# Install and use a ruby environment
rbenv install RUBY_VERSION
rbenv global RUBY_VERSION
# Install bundler and install the blog's dependencies
gem install bundler
bundle install
Serve the blog
bundle exec jekyll serve
Your changes are visible at http://localhost:4000/
1.3. Ignore the files Jekyll has created
In your .gitignore
_site/
2. Use GitLab CI to build and deploy your website
In your .gitlab-ci.yml
image: ruby:RUBY_VERSION
pages:
script:
- gem install bundler
- bundle install
- bundle exec jekyll build -d public
artifacts:
paths:
- public
And push
git add .
git commit -m "Gitlab pages setup"
git push
The pipeline deploying your project is visible on your project's home on GitLab, under Build/Pipelines.
3. Visit your website
On your project's home on GitLab, navigate to Deploy/Pages to see your website's URL.
Uncheck Use unique domain if you want your blog to be accessible from a more user-friendly URL, but keep in mind HTTPS won't work for.gitlab.io domains if your username or project name contains a dot (you can still set up your own domain).
4. (Optional) Write your blog in Markdown
One major feature of Jekyll is to translate Markdown files into an HTML website.
4.1. Tell Jekyll you'll be using Markdown
In _config.yml
markdown: kramdown
baseurl: /<name_of_your_gitlab_project>
4.2. Change your article to Markdown
Remove the file articles/my-first-article.html
In articles/my-first-article.md
---
---
# Hi Mom
Hope you're fine
Run bundle exec jekyll build and you should find your translated HTML file under _site/articles/my-first-article.html
<h1 id="hi-mom">Hi Mom</h1>
<p>Hope youβre fine</p>
This page is missing basic HTML structure and metadata. We could write our Markdown inside an HTML canvas, but this would lead in repetitions in our code, let's take advantage of Jekyll layout features.
4.3 Define a layout for your posts
In _layouts/default.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ page.title }}</title>
<!--
You may add any stylesheet or script you'd like to use
<link rel="stylesheet" href="'/assets/main.css'">
-->
</head>
<body>
<header>
<nav><a href="{{'/' | absolute_url}}">My first blog</a></nav>
</header>
<main>
{{ content }} <!-- Your content will go here -->
</main>
</body>
</html>
And then edit the file articles/my-first-article.md and make use of the Front Matter
---
title: My first article
layout: default
---
# Hi Mom
Hope you're fine
Test it with bundle exec jekyll build or bundle exec jekyll serve, once you're confident in your changes push them and let GitLab do the rest.
5. (Optional) Preview your changes before publishing them
In a development process, we usually want to review changes we have made before publishing them. This could also apply to a blog.
5.1. Prevent the pages' publication if the pipeline does not belong to the main branch
In .gitlab-ci.yml
image: ruby:RUBY_VERSION
pages:
script:
- gem install bundler
- bundle install
- bundle exec jekyll build -d public
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # This rule prevents the pages job from being executed for a commit that is not made on the main branch
5.2. Create another job only for MR which will upload a preview version of the blog
Add a file named generate_pipeline_config.sh
# Outputs a configuration which will later be used by jekyll to generate the website and make links that points to the website-as-artifact generated
echo "url: https://$CI_PROJECT_NAMESPACE.gitlab.io"
echo "baseurl: /-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/public"
And make it executable
chmod +x generate_pipeline_config.sh
In .gitlab-ci.yml
stages: # This is new
- staging
- pages
pages:
stage: pages
script:
- gem install bundler
- bundle install
- bundle exec jekyll build -d public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # This rule prevent the pages job to be executed for commit not made on the main branch
artifacts:
paths:
- public
preview-pages: # This is almost the same job, as it is not named 'pages' the job will not publish its result on GitLab Pages
stage: staging
script:
- gem install bundler
- bundle install
# The 2 following lines are modified
- ./generate_pipeline_config.sh > pipeline_config.yml
- bundle exec jekyll build -c _config.yml,pipeline_config.yml -d public # pipeline_config.yml redefines some variable from _config.yml
rules:
- if: $CI_MERGE_REQUEST_IID # This rule to execute the job only if it is part of a merge request
environment:
name: $CI_COMMIT_REF_NAME
# The environment is a link to the artifact produced by the pipeline
url: https://$CI_PROJECT_NAMESPACE.gitlab.io/-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/public/index.html
artifacts:
paths:
- public
Create an MR with your changes.
The environment section tells GitLab you have deployed something at a certain URL.
On the MR overview it suggests the reader to visit the website you have just deployed.

Some links will not work in the preview. The artifact browsing server we are using is not made to be a web server. Contrary to the GitLab Pages server it will not return
/index.htmlif you ask for/, neither will it return/articles/my-first-article.htmlyou requested/articles/my-first-article. You can choose between writing exclusively explicit links or rewrite the url in your browser when browsing the preview
Top comments (0)