Title: GitHub Actions matrix: run jobs in parallel the right way
URL: https://codemany.com/articles/github-actions-matrix
Description: GitHub Actions matrix runs one job across many versions or systems. Learn include, exclude, fail-fast and max-parallel, and how many machines you need.
All pages as Markdown: https://codemany.com/llms.txt

---

[Home](https://codemany.com/) › [Articles](https://codemany.com/articles)

# GitHub Actions matrix strategy

A GitHub Actions matrix runs the same job many times, once for each mix of values you list, like Node versions or operating systems. GitHub starts those jobs in parallel, as far as your runners allow.

## What a matrix does

Say you want to test on Node 20, 22 and 24. You could copy the job three times. A matrix does it for you.

You list values under `strategy.matrix`. GitHub makes one job for each combo. Two lists of 3 values each make 3 × 3 = 9 jobs.

A GitHub matrix can make up to 256 jobs per workflow run. That limit is in [GitHub's Actions limits](https://docs.github.com/en/actions/reference/limits).

Matrices are good for:

- testing on many language versions, like Node, Python or Go;
- testing on many systems, like Linux and macOS;
- building for many CPUs, like x86-64 and arm64;
- splitting a long test suite into parts that run side by side.

## A first matrix example

Here is a small GitHub action matrix. It tests three Node versions:

```
name: Test
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [20, 22, 24]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test
```

`${{ matrix.node }}` is the value for this one job. GitHub also adds it to the job's name, so you see `test (20)`, `test (22)` and `test (24)`.

A matrix can have more than one key. Each key adds a new axis. Add `os: [ubuntu-latest, macos-latest]` to the example above, and you get 6 jobs: three Node versions on each system. Keep an eye on the count. Each new axis multiplies the jobs, and each job uses minutes.

## include and exclude

You don't always want every combo. Two keys help.

- **exclude** drops combos you don't need.
- **include** adds an extra combo, or adds a value to combos that match.

```
strategy:
  matrix:
    os: [ubuntu-latest, macos-latest]
    node: [20, 22, 24]
    exclude:
      - os: macos-latest
        node: 20          # skip old Node on macOS
    include:
      - os: ubuntu-latest
        node: 24
        coverage: true    # only this job uploads coverage
```

This makes 5 jobs, not 6. Then a step can check `if: matrix.coverage` to run only once.

Use `exclude` often. A smaller matrix finishes sooner, waits less in the queue and uses fewer minutes.

## fail-fast and max-parallel

Two more keys control how the matrix runs.

**GitHub Actions fail-fast** is on by default. If one matrix job fails, GitHub cancels the jobs still running or queued. That saves time when one failure means all will fail. Turn it off when you want to see every result:

```
strategy:
  fail-fast: false
  max-parallel: 2
  matrix:
    node: [20, 22, 24]
```

**max-parallel** caps how many matrix jobs run at the same time. Use it when jobs share something with a limit, like a test server or an API.

You can also set `continue-on-error` on a job. Then a failure there doesn't cancel the rest. It is handy for a job you mark as experimental.

Want to split one slow test suite? Use a matrix of shard numbers. Each job runs one part:

```
strategy:
  matrix:
    shard: [1, 2, 3, 4]
steps:
  - uses: actions/checkout@v4
  - run: npx jest --shard=${{ matrix.shard }}/4
```

Four jobs, each with a quarter of the tests. If you have four free runners, the suite finishes in about a quarter of the time.

## How many jobs run at once

A matrix of 20 jobs doesn't mean 20 jobs run at once. Two things cap it:

- **Your plan,** on GitHub-hosted runners. Each plan allows a set number of jobs at once. macOS jobs have a lower cap.
- **Your runners,** on self-hosted runners. One runner takes one job. With 3 free runners, 3 jobs run, and the rest wait.

Jobs that can't start yet wait in the queue. If they wait a long time, read [waiting for a runner](https://codemany.com/problems/github-actions-waiting-for-runner). Plan numbers are in [GitHub Actions limits](https://codemany.com/articles/github-actions-limits).

## Matrix jobs on your own machines

A matrix works the same on your own runners. Just change `runs-on`. With Codemany, each matrix job takes a fresh, [single-use runner](https://codemany.com/features/single-use-runners) on any free machine.

How many jobs fit? Each job needs about 4 GB of memory, 2 CPU cores and 15 GB of disk. An 8 GB Mac runs 1 job at a time, and a 16 GB Mac runs 3. To run more matrix jobs at once, add machines. See [how many jobs per machine](https://codemany.com/docs/machines#size).

You can even spread one matrix across your Macs and your Linux boxes:

```
jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        os: [macOS, Linux]
    runs-on: [self-hosted, "${{ matrix.os }}", codemany]
    steps:
      - uses: actions/checkout@v4
      - run: make test
```

The macOS job goes to a Mac. The Linux job goes to a Linux machine. Both run at the same time if both are free. To set one up, follow [Get started](https://codemany.com/docs/get-started).

On your own machines, a bigger matrix doesn't cost more per minute. Codemany is $20 a month for 2 machines, and each extra machine is $10 a month. More machines let more matrix jobs run side by side.

## Questions

### How many jobs can a matrix make?

Up to 256 per workflow run.

### What does fail-fast do?

It cancels the other matrix jobs when one fails. It is on by default.

### Can matrix jobs run on self-hosted runners?

Yes. Each job takes a fresh Codemany runner on any free machine.

## Related

Read next: [single-use runners](https://codemany.com/features/single-use-runners), [GitHub Actions limits](https://codemany.com/articles/github-actions-limits), [waiting for a runner](https://codemany.com/problems/github-actions-waiting-for-runner), [jobs per machine](https://codemany.com/docs/machines#size) and [Get started](https://codemany.com/docs/get-started).

[Start a 14-day trial](https://codemany.com/docs/get-started) No card needed. $20 a month for 2 machines after that.
