The matrix strategy in GitHub Actions lets you run jobs across multiple configurations simultaneously, drastically cutting down on redundant workflows.
Let’s see it in action. Imagine you need to test a Node.js application across several Node versions and operating systems.
name: Node.js Matrix Test
on: [push]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [12.x, 14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
This simple configuration tells GitHub Actions to create a unique job for every combination of os and node-version defined in the matrix. In this case, it will spin up 3 (OS) * 3 (Node versions) = 9 separate jobs, each running the same steps but with different environment variables.
The strategy block is where the magic happens. You define matrix as a map where keys are the variables you want to vary. The values are arrays of the specific configurations you want to test against. GitHub Actions then generates a Cartesian product of these arrays, creating a job for each unique combination. You can then reference these matrix variables within your job definition using the ${{ matrix.<variable_name> }} syntax. This applies to runs-on, steps, env, and even other parts of your workflow.
You can also include an exclude block within strategy to skip specific combinations that don’t make sense or are known to fail. For instance, if a particular Node version doesn’t support a specific OS, you can exclude it:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [12.x, 14.x, 16.x]
exclude:
- os: windows-latest
node-version: 12.x
This exclude rule prevents a job from running on Windows with Node.js 12.x. The matrix can also be expanded dynamically. For example, you can use the output of a previous step or a JSON file to define your matrix. This is incredibly powerful for scenarios where the exact configurations aren’t known at workflow definition time, such as testing against a list of deployed environments or different database versions.
The most surprising thing about the matrix strategy is how it handles job naming. GitHub automatically appends the matrix configuration to the job name. So, for the test job above, you’ll see distinct entries like test - ubuntu-latest - node-version 12.x, test - windows-latest - node-version 14.x, and so on, making it easy to identify which specific run succeeded or failed without digging into logs.
The include keyword allows you to add additional configurations to your matrix that aren’t simply combinations of existing ones. This is useful for adding specific, one-off test cases or environments that don’t fit the general pattern.
Once you’ve mastered the basic matrix, you’ll likely want to explore how to combine it with other workflow features, like needs and if conditions, to orchestrate more complex, multi-stage pipelines.