Ruby is a programming language you must have on your computer in order to build Jekyll locally. Ruby has various gems (or plugins) that provide various functionality. Each Jekyll project usually requires certain gems.

About Ruby

Jekyll runs on Ruby, a programming language. You have to have Ruby on your computer in order to run Ruby-based programs like Jekyll. Macs include an old system version of Ruby that’s too old for current gems, so on every platform you’ll want to install a recent Ruby (3.0 or later). See the installation pages for Mac, Windows, and Linux.

About Ruby Gems

Ruby has a number of plugins referred to as “gems.” Just because you have Ruby doesn’t mean you have all the necessary Ruby gems that your program needs to run. Gems provide additional functionality for Ruby programs. There are thousands of Rubygems available for you to use.

Some gems depend on other gems for functionality. For example, the Jekyll gem might depend on 20 other gems that must also be installed.

Each gem has a version associated with it, and not all gem versions are compatible with each other.

Rubygem package managers

Bundler is a gem package manager for Ruby, which means it goes out and gets all the gems you need for your Ruby programs. If you tell Bundler you need the jekyll gem, it will retrieve all the dependencies on the jekyll gem as well – automatically.

Not only does Bundler retrieve the right gem dependencies, but it’s smart enough to retrieve the right versions of each gem. For example, if you get the github-pages gem, it will retrieve all of these other gems. (This list is from the github-pages gem in 2016. Current versions list different gems and versions, but the idea is the same.)

github-pages-health-check = 1.1.0
jekyll = 3.0.3
jekyll-coffeescript = 1.0.1
jekyll-feed = 0.4.0
jekyll-gist = 1.4.0
jekyll-github-metadata = 1.9.0
jekyll-mentions = 1.1.2
jekyll-paginate = 1.1.0
jekyll-redirect-from = 0.10.0
jekyll-sass-converter = 1.3.0
jekyll-seo-tag = 1.3.2
jekyll-sitemap = 0.10.0
jekyll-textile-converter = 0.1.0
jemoji = 0.6.2
kramdown = 1.10.0
liquid = 3.0.6
mercenary ~> 0.3
rdiscount = 2.1.8
redcarpet = 3.3.3
RedCloth = 4.2.9
rouge = 1.10.1
terminal-table ~> 1.

See how Bundler retrieved version 3.0.3 of the jekyll gem, even though the latest version of the jekyll gem at the time was 3.1.2? That’s because github-pages pinned Jekyll to 3.0.3. The same thing happens today. The github-pages gem pins Jekyll to a 3.x version, even though Jekyll 4 is available. Bundler handles all of this dependency and version compatibility for you.

Trying to keep track of which gems and versions are appropriate for your project can be a nightmare. This is the problem Bundler solves. As explained on Bundler.io:

Bundler provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed.

Bundler is an exit from dependency hell, and ensures that the gems you need are present in development, staging, and production. Starting work on a project is as simple as bundle install.

Gemfiles

Bundler looks in a project’s “Gemfile” (no file extension) to see which gems are required by the project. The Gemfile lists the source and then any gems, like this:

source "https://rubygems.org"

gem 'github-pages'
gem 'jekyll'

The source indicates the site where Bundler will retrieve the gems: https://rubygems.org.

The gems it retrieves are listed separately on each line.

Here no versions are specified. Sometimes gemfiles will specify the versions like this:

gem 'kramdown', '1.0'

This means Bundler should get version 1.0 of the kramdown gem.

To specify a subset of versions, the Gemfile looks like this:

gem 'jekyll', '~> 2.3'

The ~> sign means greater than or equal to the last digit before the last period in the number.

Here it will get any gem equal to 2.3 but less than 3.0.

If it adds another digit, the scope is affected:

gem 'jekyll', '~> 2.3.1'

This means to get any gem equal to 2.3.1 but less than 2.4.

If it looks like this:

gem 'jekyll', '~> 3.0', '>= 3.0.3'

This will get any Jekyll gem that is at least 3.0.3 but less than 4.0.

See this Stack Overflow post for more details.

Gemfile.lock

After Bundler retrieves and installs the gems, it makes a detailed list of all the gems and versions it has installed for your project. The snapshot of all gems + versions installed is stored in your Gemfile.lock file, which might look like this (again, an example from 2016):

GEM
  remote: https://rubygems.org/
  specs:
    RedCloth (4.2.9)
    activesupport (4.2.5.1)
      i18n (~> 0.7)
      json (~> 1.7, >= 1.7.7)
      minitest (~> 5.1)
      thread_safe (~> 0.3, >= 0.3.4)
      tzinfo (~> 1.1)
    addressable (2.3.8)
    coffee-script (2.4.1)
      coffee-script-source
      execjs
    coffee-script-source (1.10.0)
    colorator (0.1)
    ethon (0.8.1)
      ffi (>= 1.3.0)
    execjs (2.6.0)
    faraday (0.9.2)
      multipart-post (>= 1.2, < 3)
    ffi (1.9.10)
    gemoji (2.1.0)
    github-pages (52)
      RedCloth (= 4.2.9)
      github-pages-health-check (= 1.0.1)
      jekyll (= 3.0.3)
      jekyll-coffeescript (= 1.0.1)
      jekyll-feed (= 0.4.0)
      jekyll-gist (= 1.4.0)
      jekyll-mentions (= 1.0.1)
      jekyll-paginate (= 1.1.0)
      jekyll-redirect-from (= 0.9.1)
      jekyll-sass-converter (= 1.3.0)
      jekyll-seo-tag (= 1.3.1)
      jekyll-sitemap (= 0.10.0)
      jekyll-textile-converter (= 0.1.0)
      jemoji (= 0.5.1)
      kramdown (= 1.9.0)
      liquid (= 3.0.6)
      mercenary (~> 0.3)
      rdiscount (= 2.1.8)
      redcarpet (= 3.3.3)
      rouge (= 1.10.1)
      terminal-table (~> 1.4)
    github-pages-health-check (1.0.1)
      addressable (~> 2.3)
      net-dns (~> 0.8)
      octokit (~> 4.0)
      public_suffix (~> 1.4)
      typhoeus (~> 0.7)
    html-pipeline (2.3.0)
      activesupport (>= 2, < 5)
      nokogiri (>= 1.4)
    i18n (0.7.0)
    jekyll (3.0.3)
      colorator (~> 0.1)
      jekyll-sass-converter (~> 1.0)
      jekyll-watch (~> 1.1)
      kramdown (~> 1.3)
      liquid (~> 3.0)
      mercenary (~> 0.3.3)
      rouge (~> 1.7)
      safe_yaml (~> 1.0)
    jekyll-coffeescript (1.0.1)
      coffee-script (~> 2.2)
    jekyll-feed (0.4.0)
    jekyll-gist (1.4.0)
      octokit (~> 4.2)
    jekyll-mentions (1.0.1)
      html-pipeline (~> 2.3)
      jekyll (~> 3.0)
    jekyll-paginate (1.1.0)
    jekyll-redirect-from (0.9.1)
      jekyll (>= 2.0)
    jekyll-sass-converter (1.3.0)
      sass (~> 3.2)
    jekyll-seo-tag (1.3.1)
      jekyll (~> 3.0)
    jekyll-sitemap (0.10.0)
    jekyll-textile-converter (0.1.0)
      RedCloth (~> 4.0)
    jekyll-watch (1.3.1)
      listen (~> 3.0)
    jemoji (0.5.1)
      gemoji (~> 2.0)
      html-pipeline (~> 2.2)
      jekyll (>= 2.0)
    json (1.8.3)
    kramdown (1.9.0)
    liquid (3.0.6)
    listen (3.0.6)
      rb-fsevent (>= 0.9.3)
      rb-inotify (>= 0.9.7)
    mercenary (0.3.5)
    mini_portile2 (2.0.0)
    minitest (5.8.4)
    multipart-post (2.0.0)
    net-dns (0.8.0)
    nokogiri (1.6.7.2)
      mini_portile2 (~> 2.0.0.rc2)
    octokit (4.2.0)
      sawyer (~> 0.6.0, >= 0.5.3)
    public_suffix (1.5.3)
    rb-fsevent (0.9.7)
    rb-inotify (0.9.7)
      ffi (>= 0.5.0)
    rdiscount (2.1.8)
    redcarpet (3.3.3)
    rouge (1.10.1)
    safe_yaml (1.0.4)
    sass (3.4.21)
    sawyer (0.6.0)
      addressable (~> 2.3.5)
      faraday (~> 0.8, < 0.10)
    terminal-table (1.5.2)
    thread_safe (0.3.5)
    typhoeus (0.8.0)
      ethon (>= 0.8.0)
    tzinfo (1.2.2)
      thread_safe (~> 0.1)

PLATFORMS
  ruby

DEPENDENCIES
  github-pages
  jekyll

BUNDLED WITH
   1.11.2

Commit Gemfile.lock along with your project so that everyone who builds the site, including your CI system, uses the same gem versions. To move to the latest versions that your Gemfile allows, run bundle update, which ignores the versions in Gemfile.lock and writes a new one.

Whenever you run Jekyll, put bundle exec in front of the command (for example, bundle exec jekyll serve). That way Jekyll runs with the gem versions in Gemfile.lock instead of whatever versions happen to be installed on your computer.

To learn more about Bundler, see the Bundler documentation.