CI Setup with GitHub Actions for Ruby

· 3 min read · Updated March 17, 2026 · intermediate
ruby github-actions ci testing devops

Continuous Integration (CI) is essential for maintaining healthy Ruby projects. GitHub Actions provides a powerful, free way to automate your CI pipeline. In this guide, you’ll set up a complete CI workflow for a Ruby project.

Why GitHub Actions?

GitHub Actions integrates seamlessly with Ruby repositories. You get:

  • Free minutes for public and private repositories
  • Matrix builds to test across multiple Ruby versions
  • Caching to speed up your workflows
  • Native integration with GitHub’s ecosystem

Basic CI Workflow

Create .github/workflows/ci.yml in your repository:

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.3'
          bundler-cache: true

      - name: Install dependencies
        run: bundle install

      - name: Run tests
        run: bundle exec rake

This workflow runs on every push and pull request to main.

Testing Multiple Ruby Versions

Use a matrix strategy to test across Ruby versions:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3']

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby-version }}
          bundler-cache: true

      - name: Install dependencies
        run: bundle install

      - name: Run tests
        run: bundle exec rake

Each Ruby version runs as a separate job. Set fail-fast: false to let all versions complete even if one fails.

Adding Linting

Include RuboCop in your CI pipeline:

  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.3'
          bundler-cache: true

      - name: Install RuboCop
        run: bundle add rubocop --skip-install && bundle install

      - name: Run RuboCop
        run: bundle exec rubocop

Run linting in parallel with tests:

jobs:
  test:
    # ... test steps

  lint:
    # ... lint steps

  ci:
    needs: [test, lint]

Caching Dependencies

Speed up your workflows with caching:

- name: Set up Ruby
  uses: ruby/setup-ruby@v1
  with:
    ruby-version: '3.3'
    bundler-cache: true
    cache-version: ${{ hashFiles('Gemfile.lock') }}

The bundler-cache: true option automatically caches gems between runs.

Running Database Tests

For Rails projects, set up a database service:

services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: test
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5
    ports:
      - 5432:5432

env:
  DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
  RAILS_ENV: test

Complete Example

Here’s a production-ready workflow for a Rails application:

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    strategy:
      fail-fast: false
      matrix:
        ruby-version: ['3.1', '3.2', '3.3']

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby-version }}
          bundler-cache: true

      - name: Install dependencies
        run: bundle install

      - name: Set up database
        run: bin/rails db:test:prepare

      - name: Run tests
        run: bundle exec rake

  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.3'
          bundler-cache: true

      - name: Run RuboCop
        run: bundle exec rubocop

Best Practices

  1. Use latest actions versionsruby/setup-ruby@v1 and actions/checkout@v4
  2. Cache bundler — Always enable bundler-cache: true
  3. Test multiple Ruby versions — Use matrix strategy for compatibility
  4. Separate concerns — Run linting and tests as separate jobs
  5. Fail-fast carefully — Use fail-fast: false for comprehensive feedback

See Also