Ruby in GitLab CI

The GitLab file

GitLab reads one file from the repository root, .gitlab-ci.yml. Jobs are named by the keys, and a job runs inside a container image; for a Ruby project the image is ruby:3.4 and the script is the same pair of commands:

stages: - test default: image: ruby:3.4 cache: key: files: - Gemfile.lock paths: - vendor/bundle test: stage: test script: - bundle config set --local path vendor/bundle - bundle install - bundle exec rake test

The cache block is explicit here: the key is the lock file, and the cached path is the local bundle. That is the same idea as GitHub bundler cache, written out by hand, which is why this lesson shows the folder as well as the file.

Check that it parses

Parse the file before pushing it. The standard library reads both CI formats, so the check is one command:

ruby -ryaml -e 'YAML.load_file(ARGV[0])' .gitlab-ci.yml

No output means it parsed. Asking for the script list shows the three commands GitLab will run in order:

ruby -ryaml -e 'p YAML.load_file(ARGV[0])["test"]["script"]' .gitlab-ci.yml

["bundle config set --local path vendor/bundle", "bundle install", "bundle exec rake test"]

The same steps by hand

Run the three commands in the project folder, in the same order:

Put the bundle inside the project

bundle config set --local path vendor/bundle

The command prints nothing; it writes .bundle/config. Lesson 55, later, opens that file.

Install the gems

bundle install

Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Fetching rake 13.4.2
Installing rake 13.4.2
Fetching minitest 5.27.0
Installing minitest 5.27.0
Bundle complete! 2 Gemfile dependencies, 2 gems now installed.
Bundled gems are installed into `./vendor/bundle`

Run the tests

bundle exec rake test

Run options: --seed 29433

# Running:

...

Finished in 0.001215s, 2469.1155 runs/s, 3292.1540 assertions/s.

3 runs, 4 assertions, 0 failures, 0 errors, 0 skips

The output above comes from running the three commands locally, not from a GitLab runner. The pipeline log repeats the same text with the job header around it.

The ruby image

A GitLab runner starts each job in a container, so the same image can be tried on any machine with Docker. The tag decides the Ruby major version; 3.4 is the line this course runs:

docker run --rm ruby:3.4 ruby -v

ruby 3.4.12 (2026-10-23 revision 366f5fdc00) +PRISM [x86_64-linux]

That is the version inside the image, not the version on the host. The difference is the point of the image: the job does not care what the runner has installed.

What to watch for

The cache key must change when the lock file changes, and only then. A key of Gemfile.lock makes GitLab hash the file for you: new gems, new key; a new test file, same key.

Cache both vendor/bundle and .bundle/config if you set the path in the job, or the next run installs the gems again and wonders where they went. Never cache .bundle without the path it names.

Next: Rake as the pipeline runner

Article author: Arthur Isaev