Matrix builds

GitHub Actions · Practical · 10 min

Testing every supported platform and version by hand is impossible; a matrix does it in parallel from a few lines.

strategy.matrix turns one job into many. Each key is a variable with a list of values, and GitHub creates one job per combination, reading values as ${{ matrix.<key> }}.

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [22, 24]

That is 3 x 2 = 6 jobs. Fine-tuning:

  • include: add extra combinations or add keys to matching ones (for example an experimental: true flag).
  • exclude: drop specific combinations.
  • fail-fast (default true): cancel the remaining matrix jobs when one fails. Set false to see every failure.
  • max-parallel: limit how many run at once.

A matrix can generate up to 256 jobs per workflow run. Name the job with matrix values, name: test (${{ matrix.os }}, node ${{ matrix.node }}), so failures are readable. If branch protection requires a check, it must name the exact job names produced by the matrix.

The matrix can also be dynamic: one job computes a JSON list and outputs it, and the next uses matrix: ${{ fromJSON(needs.plan.outputs.targets) }}, which is how monorepos test only the packages that changed.

Example

jobs:
  test:
    name: test (${{ matrix.os }}, py ${{ matrix.python }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
        python: ["3.13", "3.14"]
        exclude:
          - os: macos-latest
            python: "3.13"
        include:
          - os: ubuntu-latest
            python: "3.15"
            experimental: true
    continue-on-error: ${{ matrix.experimental == true }}
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with: {python-version: "${{ matrix.python }}"}
      - run: python -m pytest       # 4 jobs in total

Remember

  • Each matrix combination is a separate parallel job
  • include adds or extends combinations; exclude removes them
  • fail-fast: false shows every failing combination

Watch out for

  • Unquoted versions like 3.10 become the number 3.1 in YAML.

Check yourself

  1. matrix: os: [a, b], v: [1, 2, 3] with exclude of one combination. How many jobs run?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow