A personal website is a great way to improve your online presence. There are lots of affordable options to design and publish such a website, and I thought it might be useful to document the particular decisions I took when designing the website your are browsing right now. My aim here is to compile all necessary information to go the same way, in particular those bits and pieces I had to dig for.

General Requirements

Following my preferences, I had the following requirements for the system:

  • it should be possible and easy to put everything under version control, preferably git
  • editing the website should be possible locally with a standard text editor, rather than online through a web platform
  • it should be flexible in terms of layouts, etc. I want control!
  • but remain simple enough to work with. I am no front-end engineer.

That does sound pretty restrictive, but there are quite a few options that obey those constraints: static site generators. A static site generator is a piece of software that converts files in a nice format (markdown, mostly) to a set of html files, ready to be served by a web server.

Jekyll is a very popular option, and after trying it out, I liked it so much I did not really look at other options.

Minimal mistakes is a very popular theme for it, for good reasons: it looks amazing out of the box, is very modular and easy to extend, and already includes the most popular alternatives for blog comments, analytics and SEO.

Small additions

Portrait from Gravatar

Gravatar is a great service that allows you to display a consistent avatar across services. It works by associating a picture of your choice to your e-mail. I love it: I have the same avatar on GitHub, various GitLab instances, and Slack, between others, without even having to think in it.

Wouldn’t it be great to be able to have that same avatar on your own website? Nothing easier!

The Gravatar API to retrieve the avatar linked to an e-mail is actually very simple. The process is the following:

  • take your e-mail address, without trailing whitespace, in lower case
  • compute its MD5 hash. There are for instance some web services that do this for you.
  • your profile picture is available at https://www.gravatar.com/avatar/HASH?s=SIZE, where HASH is the hash previously calculated, and SIZE is the desired size (in pixels) of the picture (optional)

Just put that URL in your _config.yml, where the path to the picture should go… and you are done!

List of Publications with Jekyll-Scholar

An important part of the CV of an academic is the list of publications. It should be complete and consistently formated. Typically, I use LaTeX and bibtex to track and format my references. How nice it would be to simply be able to use that same data for the website!

Jekyll has a nice extension named Jekyll-Scholar that can convert a bibtex file to a nice looking html list.

Unfortunately, by default, Jekyll scholar does not include a link to the paper, even if your bibtex entry has a “url” attribute. Fortunately, that is easy to add, using the information from this GitHub issue.

In essence, you can specify any arbitrary template for displaying the references. In my case, I designed this one, placed in _layouts/bib.html:

{{ reference }}

{% if entry.url and entry.url != "" %}
  <a href="{{ entry.url }}" class="btn btn--primary">
{% endif %}

Tell Jekyll scholar where to find it by adding the following to your _config.yml:

    # template for references in _layout/bib.html
    bibliography_template: bib

and you are done!

Adding Comments

Jekyll generates a static website: a set of html pages your browser can display. There is no database backend of any sort. This has lots of advantage: no need for a big server, less risks of failures, easy to version-control. But this does not allow comments, which is kind of sad for a blog…

Given the popularity of Jekyll as a blog platform, various solutions exist to include comments. Here are, from my research, the most popular:

  • Disqus is a web service that allows you to store comments, moderate them, with all kinds of bells and whistles. It has a free offering, but users reported being annoyed by its displaying of adds.
  • Staticman is a web services that gets comments and commits them to the git repository containing your website. It helps to keep everything in one place, but only works for GitHub, and although lots of people love it, I find the idea of comitting the comments to my repository awkward…
  • utterances is a very simple service that uses GitHub issues as a platform to store comments. It is lightweight and open source. I really like the idea, and it integrates well with websites hosted on GitHub pages. However, it does require the readers to have a GitHub account for commenting…
  • Discourse is a platform for building forums. It is open source and relatively easy to self-host.

I opted for Disqus due to its ease of use, but I am planning to deploy my own instance of Discourse on a spare VPS I have, when time allows.

In the long run, I would hope a solution based on Solid appears (more on this in a future post!).

Deploying with Gitlab

Gitlab Pages vs. GitHub Pages

The most popular solution to host personal websites based on Jekyll seem to be GitHub pages. I can understand: GitHub pages supports jekyll by default, so all you have to do is create a new repository, enable GitHub pages, push your jekyll site in there, and enjoy the show. GitHub will re-build the site on each push, which is very convenient. However, you do not have control on the process. In particular, there are only a few Jekyll plugins that are whitelisted for use in GitHub pages… That is a no-go for me.

GitLab, GitHub’s contender, offers a very similar service, originally named “GitLab Pages”. However, it is, in my opinion, much nicer, because it relies on GitLab’s continuous integration service, GitLab CI, for deployment. GitLab’s repository size limit is also set to 10GB, which is definitely an advantage over GitHub’s 1GB when speaking of a repository that is deemed to contain images with reasonable resolution.

Gitlab CI

GitLab CI is GitLab’s continuous integration and deployment service. It is very similar to what you can get in GitHub by using Travis, but it is part of the GitLab platform instead of being an external service. This is actually one of the main selling points of GitLab when trying to convert GitHub users (aside from the fact of not being owned by Microsoft): whereas GitHub mostly focuses on being a good git platform, delegating other tasks to external services (for instance CI to Travis et al.), GitLab’s current motto is “A full DevOps toolchain (no assembly required)” — all necessary services are part of GitLab itself. This allows for a tighter integration of those services, and their workflow for deploying pages is a perfect example.

GitLab's Motto
GitLab tries to differentiate itself from GitHub by providing the whole DevOps toolchain itself, rather than delegating to third-party services. Screenshot from gitlab.com

GitLab Pages works by letting GitLab CI build an artifact that is then deployed to a webserver. Why is it nice? Because GitLab does not care about how the heck you generate those html files! The configuration file allows you to use any docker container, and execute any set of commands in it, to generate your site. This means you do not have any limitation as to the software, plugins or versions of those you use, contrary to GitHub. You do not even have to limit yourself to Jekyll! You can use any pipeline that ends up generating html files from what is available in your repository. GitLab for instance has a nice blog post on how they use GitLab Pages and GitLab CI to generate test coverage reports.

Adding Tests

Websites are messy things: they contain lots of links, both internal and external, that are easy to break (or get wrong from the first time), and invisible factors might influence how they rank in search engines. Wouldn’t it be nice to be able to check some of those potential problems automatically? Of course! Is there an easy way? You bet there is!

html-proofer is a ruby gem that does just that: it checks for broken links, missing alt tags in images, presence of open graph metadata, you name it. And it is incredibly easy to use, too! I found the easiest way to use it was to use it as a rake test, as recommended in the documentation, and add a GitLab CI job that runs it. Beware though: contrary to what is said in the documentation, the “deploy” stage for pages will be run whenever the “pages” job succeeds, even if other jobs in the same or subsequent stages fail. A solution is to run tests in the “build” stage and pages in the “test” stage, even if that sounds awkward… Or defining custom stages (which is what I did). For reference, my .gitlab-ci.yml looks like this:

image: jekyll/jekyll:latest

    - build
    - test
    # pages is a strange job, in that the deploy:pages runs if it succeeds, event if jobs in the same or subsequent phases fail
    # -> put in its own phase just before deploy
    - pages
    - deploy

        - vendor/

    stage: test
        JEKYLL_ENV: development
        - bundle install --path vendor/bundle
        - bundle exec rake test

    stage: pages
        JEKYLL_ENV: production
        - bundle install --path vendor/bundle
        - jekyll build --verbose -d public
            - public
        - master

For reference, my Rakefile looks like this:

require 'html-proofer'

task :test do
  sh "bundle exec jekyll build"
  options = {
      # allows empty hash refs
      :allow_hash_href => true,

      # Linkedin returns a 999 when probing links.
      # It seems to be some protection that cannot be circumvented easily
      # (changing the user agent does not help)
      # Just add any other domain that fails but is correct here
      :url_ignore => [/www.linkedin.com/],

      # Check opengraph metadata, as minimal mistakes adds it
      :check_opengraph => true,

      # Not only check page, but also if the hash exists
      :check_external_hash => true,

      # make all internal links explicitly internal.
      # Minimal Mistakes otherwise generates cannonical links
      # with domain included, which fails as long as page not published yet...
      :url_swap => {"https://thibaut.dubernet.ch/" => "/"},
  HTMLProofer.check_directory("./_site", options).run

Using Gitlab CI to Have and Up-to-date CV

If you are like me, you version control everything. This means, of course, that you have one git repository with the latest version of your CV (and, because you are like me, it is of course written in LaTeX). Wouldn’t it be nice to have that shiny CV always up-to date on your website? With GitLab CI, nothing easier!

You can just add a CI job that generates your CV and deploys it on each push to the master branch. Note that if your repository is private (which might be a good idea for application documents), the only way to get public artifacts is to go through GitLab pages again. So run it in a job named pages, and move it to the public/ directory. Do not forget to put a dummy html file in that public directory as well, otherwise your document will not be accessible.

Now you only need to link to that document from your website, and voilà!, each time you push a new version of your CV to master, it will automatically be available from your personal website!

Something similar would probably be feasible using GitHub, but the setup is much more complicated, due to the back-and-forth between third party services. Here is for instance how you would do it using Travis.

Summing it Up

In this post, I documented some of the design decision I did for this website. I hope this can be useful to anybody coming up with the same requirements. I am particularly happy about the continuous deployment setup, which allows me to keep my CV separate from my website, yet make sure I always have the latest version online, and makes sure my website cannot go online with broken links or metadata.


Want to react? Send me an e-mail or use Webmentions


No webmentions were found.