Advanced Matrix Builds

Go beyond a simple grid with include, exclude, and dynamic matrices generated at runtime.

You already know a matrix runs a job across combinations of values. The senior techniques are shaping that grid precisely and even building it on the fly.

include and exclude

  • exclude — remove specific combinations from the grid (e.g. skip an old Node version on Windows).
  • include — add extra properties to matching combinations, or append one-off entries that aren't part of the cross-product.

fail-fast and max-parallel

fail-fast: false lets every combination finish so you see all failures, not just the first. max-parallel caps concurrency when you want to be gentle on a rate-limited service.

Dynamic matrices

A job can emit JSON as an output, and a downstream job can consume it as its matrix. This lets you generate the build grid from changed files, a config file, or an API call — the matrix is computed, not hard-coded.

Example

Example · yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      max-parallel: 4
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: [18, 20, 22]
        exclude:
          - os: windows-latest
            node: 18            # skip this combo
        include:
          - os: ubuntu-latest
            node: 22
            experimental: true  # extra flag for one cell
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm test

When to use it

  • A library maintainer uses fail-fast: false so a failing Node 18 run does not cancel the Node 20 and 22 runs still in progress.
  • A team uses matrix includes to inject environment-specific variables into individual matrix combinations without creating separate job definitions.
  • A cross-platform CLI adds a per-OS binary extension variable to the matrix so the upload step uses the correct filename on each runner.

More examples

Disable fail-fast for full matrix coverage

fail-fast: false lets all six matrix jobs run to completion even if one fails, giving you full visibility across all combinations.

Example · yaml
strategy:
  fail-fast: false
  matrix:
    node: [18, 20, 22]
    os: [ubuntu-latest, windows-latest]
jobs:
  test:
    runs-on: ${{ matrix.os }}
    steps:
      - run: node --version

Add extra variables with matrix include

The include list attaches arbitrary properties (ext, label) to each OS entry so the upload step can form the correct filename per platform.

Example · yaml
strategy:
  matrix:
    include:
      - os: ubuntu-latest
        ext: ''
        label: linux
      - os: windows-latest
        ext: '.exe'
        label: windows
steps:
  - run: mv dist/app dist/app-${{ matrix.label }}${{ matrix.ext }}
  - uses: actions/upload-artifact@v4
    with:
      name: binary-${{ matrix.label }}
      path: dist/app-${{ matrix.label }}${{ matrix.ext }}

Pass max-parallel to control concurrency

max-parallel caps the simultaneous runners to three, useful when an external resource (browser pool, licence server) has limited capacity.

Example · yaml
strategy:
  matrix:
    shard: [1, 2, 3, 4, 5, 6]
  max-parallel: 3
jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - run: npx playwright test --shard=${{ matrix.shard }}/6

Discussion

  • Be the first to comment on this lesson.
Advanced Matrix Builds — GitHub | SoundsCode