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