Guides

Getting Started

Guides

Dependency Management

Gem Development

Publishing & Security

Integrations

Hosting & Sources

Extending

Troubleshooting

Concepts

Reference

Appendix

How to use Bundler in CI

How to install gems quickly and reproducibly on GitHub Actions, GitLab CI, and other CI systems.

A CI job needs the same gems as your development machine, installed from scratch on every run. The recipe is the same everywhere. Cache installed gems, keyed on the Gemfile.lock when the repository has one, and configure Bundler through BUNDLE_* environment variables instead of running bundle config in each job.

GitHub Actions

Use the ruby/setup-ruby action. With bundler-cache: true it runs bundle install for you and caches the installed gems, so you do not need a separate gem install bundler or bundle install step. When a Gemfile.lock is present, it installs in deployment mode, so the job fails if the lockfile is out of date instead of silently resolving new versions.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true
      - run: bundle exec rake

The ruby-version input can be omitted if the repository has a .ruby-version, .tool-versions, or mise.toml file. To test against several Ruby versions, use a matrix:

jobs:
  test:
    strategy:
      matrix:
        ruby: ["3.3", "3.4", "4.0"]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: $
          bundler-cache: true
      - run: bundle exec rake

To test against multiple Gemfiles, set BUNDLE_GEMFILE in a job-level env block (for example from a matrix value) so that both setup-ruby and later steps use the same Gemfile.

GitLab CI

On GitLab, install gems into the project directory and cache that path, keyed on Gemfile.lock so the cache is invalidated when dependencies change:

test:
  image: ruby:4.0
  cache:
    key:
      files:
        - Gemfile.lock
    paths:
      - vendor/ruby
  script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install
    - bundle exec rake

Setting BUNDLE_PATH: vendor/ruby and BUNDLE_FROZEN: "true" in the variables block is an equivalent alternative to the bundle config line, and keeps the configuration visible in one place.

Dependency bots and cooldown

If CI runs against pull requests opened by a dependency bot, align the bot’s update delay with the cooldown configured in Bundler. Otherwise the bot proposes versions that bundle install refuses to resolve, or bypasses the waiting period that cooldown is meant to enforce.

On GitHub, Dependabot supports a cooldown block for version updates in dependabot.yml:

version: 2
updates:
  - package-ecosystem: "bundler"
    directory: "/"
    schedule:
      interval: "weekly"
    cooldown:
      default-days: 7

The cooldown block also accepts semver-major-days, semver-minor-days, and semver-patch-days to set different delays per update type, and include and exclude lists to scope which dependencies it applies to.

Renovate, which runs on both GitHub and GitLab, offers the same control through minimumReleaseAge, a duration string such as "7 days". Set it in renovate.json, either at the top level or scoped to Bundler:

{
  "packageRules": [
    {
      "matchManagers": ["bundler"],
      "minimumReleaseAge": "7 days"
    }
  ]
}

In both cases, use the same number of days as your Bundler cooldown. See the cooldown guide for how the Bundler side works.

Publishing from CI

To release gems from a CI pipeline without long-lived API keys, see Trusted Publishing.