Skip to main content

How to run matrix builds in CI

Use a CI build matrix to test code across multiple language versions and operating systems in parallel, with include/exclude rules and fail-fast control.

Difficulty
Intermediate
Duration
35 minutes
Steps
6

What and why

A build matrix runs the same job many times with different parameters, in parallel. It is the standard way to confirm code works across several language versions and operating systems without copying a job definition for each combination. Libraries especially need this: users run them on many runtimes, so the maintainer must test on all of them. A matrix turns a combinatorial testing problem into a few lines of configuration.

The matrix also surfaces version-specific bugs early. A test that passes on the newest runtime but fails on an older supported one is exactly the kind of regression a matrix is designed to catch before release.

Prerequisites

  • A CI workflow that already builds and tests your code.
  • Code that genuinely supports the multiple runtime versions you list.
  • Basic YAML knowledge.

Steps

1. Add a strategy matrix

Under a job, add a strategy.matrix block listing the values to vary:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: ['18', '20', '22']

This expands the single job into three jobs, one per value, each running concurrently. The matrix values appear in the run's job names so failures are easy to attribute.

2. Use matrix values in steps

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci && npm test

Reference each value with the matrix context. Every expanded job receives its own value, so the same step installs a different runtime in each.

3. Combine multiple dimensions

    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: ['20', '22']
    runs-on: ${{ matrix.os }}

Multiple keys multiply together. Three operating systems times two runtime versions produce six parallel jobs covering the full cross-product. Setting runs-on from a matrix value is how you test across operating systems.

4. Exclude and include entries

    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: ['18', '20']
        exclude:
          - os: windows-latest
            node: '18'
        include:
          - os: macos-latest
            node: '22'

exclude removes a specific combination you do not support, trimming wasted runs. include adds an extra entry that is not part of the cross-product, useful for testing one special case without expanding every dimension.

5. Tune fail-fast and concurrency

    strategy:
      fail-fast: false
      max-parallel: 4
      matrix:
        node: ['18', '20', '22']

By default fail-fast cancels remaining jobs as soon as one fails. Setting it to false lets every job finish, so you see all failures in one run instead of fixing them one at a time. max-parallel caps how many jobs run at once, which is useful when you have limited runner capacity.

Verification

Push the change and open the run. The single job should expand into one entry per matrix combination, each labeled with its values. Confirm that any excluded combination is absent and any included one is present. Introduce a failure on one version with fail-fast: false and confirm the other versions still complete, demonstrating that you get a full picture of failures.

Next Steps

Generate a dynamic matrix from the JSON output of a prior job when the set of versions is computed rather than fixed. Use matrix values to build and publish platform-specific artifacts. To save minutes, run a small matrix on pull requests and the full matrix only on the main branch or before a release.

Prerequisites

  • A working CI workflow
  • An app that supports several runtime versions
  • Basic YAML knowledge

Steps

  • 1
    Add a strategy matrix
  • 2
    Use matrix values in steps
  • 3
    Combine multiple dimensions
  • 4
    Exclude and include entries
  • 5
    Tune fail-fast and concurrency
  • 6
    Verify all combinations run

Category

CI/CD