35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English 35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English
GitHub Actions voor Rails in 2026: Een CI/CD Pipeline Die Écht Werkt

GitHub Actions voor Rails in 2026: Een CI/CD Pipeline Die Écht Werkt

TTB Software
devops
Een complete GitHub Actions CI/CD pipeline voor Rails: parallelle tests, service containers, lint-first strategie en deployment. Onder 5 minuten, beproefd in productie.

De meeste Rails CI-pipelines die ik overneem als fractional CTO zien er hetzelfde uit: traag, instabiel, en bij elkaar gehouden met plakband. Een build van 20 minuten die willekeurig faalt op systeemtests. Developers die met gekruiste vingers naar main pushen.

Dit is de pipeline die ik in 2026 op elk nieuw Rails-project deploy. Hij draait in minder dan 5 minuten, vangt echte problemen op, en deployt met vertrouwen.

De Complete Workflow

# .github/workflows/ci.yml
name: CI

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

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true
      - run: bundle exec rubocop --parallel
      - run: bundle exec brakeman --no-pager

  test:
    runs-on: ubuntu-latest
    needs: lint
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_PASSWORD: postgres
        ports: ["5432:5432"]
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7
        ports: ["6379:6379"]
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    strategy:
      fail-fast: false
      matrix:
        ci_node: [0, 1, 2, 3]

    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Setup database
        env:
          DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
          RAILS_ENV: test
        run: bin/rails db:setup

      - name: Run tests (split)
        env:
          DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
          REDIS_URL: redis://localhost:6379/0
          RAILS_ENV: test
          CI_NODE_TOTAL: 4
          CI_NODE_INDEX: ${{ matrix.ci_node }}
        run: |
          bundle exec rails test $(
            find test -name "*_test.rb" | sort | \
            awk "NR % $CI_NODE_TOTAL == $CI_NODE_INDEX"
          )

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        run: |
          # Jouw deployment commando hier
          # kamal deploy, cap production deploy, etc.
          echo "Deploying..."

Laten we de belangrijkste beslissingen doorlopen.

Concurrency Control Bespaart Geld

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Als je drie commits snel achter elkaar pusht, heb je geen drie CI-runs nodig. Dit annuleert lopende runs voor dezelfde branch. Bij een druk team met 30+ PR’s per dag scheelt dit 30-40% op je Actions-rekening.

Eerst Linten, Dan Testen

De needs: lint dependency zorgt ervoor dat tests pas starten als linting slaagt. Waarom 4 parallelle runners verspillen aan een testsuite als er een Brakeman-waarschuwing is die de merge toch blokkeert?

Rubocop met --parallel gebruikt alle beschikbare cores. Op GitHub’s runners zijn dat er meestal 2, wat de linttijd ruwweg halveert.

Parallelle Test-Splitsing Zonder Betaalde Tools

De meeste guides wijzen je naar Knapsack Pro of CircleCI’s test splitting. Die werken prima, maar je krijgt 80% van het voordeel gratis:

find test -name "*_test.rb" | sort | \
  awk "NR % $CI_NODE_TOTAL == $CI_NODE_INDEX"

Dit verdeelt testbestanden round-robin over nodes. Het is niet tijdsgebalanceerd zoals Knapsack, maar door te sorteren is de verdeling deterministisch. Met 4 nodes ga je typisch van 12 minuten naar 3-4 minuten.

Wanneer upgraden naar Knapsack Pro: Als je traagste node 2x langer duurt dan je snelste. Dan zijn je testbestanden te ongelijk voor round-robin.

Service Containers Goed Instellen

Een veelgemaakte fout: geen health checks instellen op service containers. Zonder die checks starten je tests voordat Postgres klaar is, en krijg je instabiele verbindingsfouten.

options: >-
  --health-cmd pg_isready
  --health-interval 10s
  --health-timeout 5s
  --health-retries 5

GitHub Actions wacht tot alle health checks slagen voordat steps uitgevoerd worden. Dit elimineert een hele categorie “werkt lokaal, faalt in CI” bugs.

De fail-fast: false Beslissing

strategy:
  fail-fast: false

Standaard annuleert GitHub alle matrix-jobs als er één faalt. Dat klinkt efficiënt, maar het is verschrikkelijk voor debugging. Als node 2 faalt maar node 0 geannuleerd wordt, weet je niet of je één falende test hebt of twintig.

Zet fail-fast: false. Laat alle nodes afronden. Ken de volledige omvang van de schade.

Caching Die Écht Werkt

De ruby/setup-ruby action met bundler-cache: true regelt gem-caching automatisch. Schrijf geen eigen cache-keys voor bundler — de officiële action doet het beter en handelt cache-invalidatie op Gemfile.lock-wijzigingen af.

Voor Node.js assets (als je assets:precompile draait):

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: yarn

- run: yarn install --frozen-lockfile

Systeemtests: Draai Ze Apart

Als je systeemtests hebt (Capybara + headless Chrome), draai ze in een aparte job:

system-test:
  needs: test
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: ruby/setup-ruby@v1
      with:
        bundler-cache: true
    - uses: actions/setup-node@v4
      with:
        node-version: 22
        cache: yarn
    - run: yarn install --frozen-lockfile
    - name: Systeemtests
      env:
        DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
        RAILS_ENV: test
      run: |
        bin/rails db:setup
        bin/rails test:system
    - uses: actions/upload-artifact@v4
      if: failure()
      with:
        name: screenshots
        path: tmp/screenshots/

De upload-artifact bij failure is cruciaal. Als een systeemtest faalt, krijg je de screenshot. Zonder debug je blind.

Branch Protection Rules

Een pipeline is zo goed als zijn handhaving. In je repo-instellingen:

  1. Vereis dat status checks slagen — selecteer de lint en test jobs
  2. Vereis dat branches up-to-date zijn — voorkomt het mergen van verouderde branches
  3. Vereis lineaire history — squash of rebase, nooit merge commits

Je pipeline zou ook zero-downtime migratiechecks moeten bevatten als onderdeel van de deploy-stap — strong_migrations draaien in CI vangt gevaarlijke migraties af voordat ze productie bereiken.

Dit voorkomt het “maar het slaagde op mijn branch” probleem waarbij twee PR’s individueel slagen maar breken wanneer ze gecombineerd worden.

Secrets Management

Gebruik geen repository secrets voor environment-specifieke config. Gebruik GitHub Environments:

deploy:
  environment: production
  steps:
    - run: kamal deploy

Environments geven je:

  • Verplichte reviewers — iemand moet productie-deploys goedkeuren
  • Wachttijden — automatische vertraging voordat deploy start
  • Deployment logs — wie deployde wat, wanneer

Wat Dit Oplevert

Met deze setup ziet een typische PR-cyclus er zo uit:

  1. Push → Lint draait (30 seconden)
  2. Lint slaagt → 4 test-nodes starten (3-4 minuten)
  3. Alles groen → Systeemtests draaien (2-3 minuten)
  4. Merge → Auto-deploy triggert

Totale tijd van push tot gedeployd: minder dan 8 minuten. Dat is snel genoeg dat developers niet van context wisselen terwijl ze wachten.

Combineer dit met feature flags voor riskante wijzigingen, en je deployment-vertrouwen gaat door het dak — zelfs op een vrijdagmiddag.

De pipeline is met opzet simpel. Elke slimme optimalisatie die ik uit CI-pipelines heb verwijderd, heeft ze betrouwbaarder gemaakt. Saaie CI is goede CI.

Veelgestelde Vragen

Hoe versnel ik een trage Rails CI-pipeline?

Begin met parallelle test-splitsing — verdeel testbestanden over 4 matrix-nodes met round-robin. Voeg bundler-cache: true toe voor gem-caching en draai linting vóór tests zodat je snel faalt op stijlproblemen. Deze drie wijzigingen brengen een pipeline van 15 minuten typisch terug naar minder dan 5 minuten. Als het nog steeds traag is, profileer je testsuite op trage individuele tests.

Moet ik GitHub Actions of CircleCI gebruiken voor Rails?

GitHub Actions is de betere standaardkeuze voor de meeste teams in 2026. Het is diep geïntegreerd met GitHub (branch protection, environments, deployments), de gratis tier is ruim, en de Actions marketplace dekt de meeste behoeften. CircleCI heeft nog voordelen in test splitting-intelligentie (Knapsack Pro integratie) en Docker layer caching, maar GitHub Actions verkleint die kloof elk jaar.

Hoe ga ik om met instabiele tests in CI?

Markeer eerst instabiele tests met een tag en volg ze. Draai niet gewoon de pipeline opnieuw — dat verbergt het probleem. Veelvoorkomende oorzaken: tijdsafhankelijke assertions, gedeelde database-state tussen tests, en systeemtests die niet wachten op asynchrone operaties. Gebruik fail-fast: false zodat je alle failures ziet, niet alleen de eerste. Los de hoofdoorzaak op in plaats van retries toe te voegen.

Is het veilig om automatisch te deployen vanuit CI bij merge naar main?

Ja, met waarborgen. Vereis dat CI status checks slagen, handhaaf branch protection rules, en gebruik GitHub Environments met verplichte reviewers voor productie-deploys. Voeg een wachttijd toe als je een afkoelperiode wilt. Deze setup zorgt ervoor dat elke merge intentioneel, getest en gereviewd is voordat het productie bereikt.

T

About the Author

Roger Heykoop is een senior Ruby on Rails ontwikkelaar met 19+ jaar Rails ervaring en 35+ jaar ervaring in softwareontwikkeling. Hij is gespecialiseerd in Rails modernisering, performance optimalisatie, en AI-ondersteunde ontwikkeling.

Get in Touch

Share this article

Need Expert Rails Development?

Let's discuss how we can help you build or modernize your Rails application with 19+ years of expertise

Schedule a Free Consultation