A matrix strategy lets you use variables in a single job definition to automatically create multiple job runs that are based on the combinations of the variables. For example, you can use a matrix strategy to test your code in multiple versions of a language or on multiple operating systems.
Using a Matrix Strategy
Use jobs.<job_id>.strategy.matrix
to define a matrix of different job configurations. Within your matrix, define one
or more variables followed by an array of values. For example, the following matrix has a variable called version
with
the value [10, 12, 14]
and a variable called os with the value [ubuntu-latest, windows-latest]
:
jobs:
example_matrix:
strategy:
matrix:
version: [10, 12, 14]
os: [ubuntu-latest, windows-latest]
A job will run for each possible combination of the variables. In this example, the workflow will run six jobs, one for
each combination of the os
and version
variables.
By default, GitHub will maximize the number of jobs run in parallel depending on runner availability. The order of the variables in the matrix determines the order in which the jobs are created. The first variable you define will be the first job that is created in your workflow run. For example, the above matrix will create the jobs in the following order:
{version: 10, os: ubuntu-latest}
{version: 10, os: windows-latest}
{version: 12, os: ubuntu-latest}
{version: 12, os: windows-latest}
{version: 14, os: ubuntu-latest}
{version: 14, os: windows-latest}
A matrix will generate a maximum of 256 jobs per workflow run. This limit applies to both GitHub-hosted and self-hosted runners.
The variables that you define become properties in the matrix context, and you can reference the property in other areas
of your workflow file. In this example, you can use matrix.version
and matrix.os
to access the current value of
version and os that the job is using. For more information, see "Contexts."
Example: Using a Single-Dimension Matrix
You can specify a single variable to create a single-dimension matrix.
For example, the following workflow defines the variable version with the values [10, 12, 14]
. The workflow will run
three jobs, one for each value in the variable. Each job will access the version value through the matrix.version
context and pass the value as node-version
to the actions/setup-node action.
Example: Using a Multi-dimension Matrix
You can specify multiple variables to create a multi-dimensional matrix. A job will run for each possible combination of the variables.
For example, the following workflow specifies two variables:
- Two operating systems specified in the
os
variable - Three Node.js versions specified in the
version
variable
The workflow will run six jobs, one for each combination of the os
and version
variables. Each job will set the
runs-on
value to the current os
value and will pass the current version value to the actions/setup-node
action.
jobs:
example_matrix:
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-20.04]
version: [10, 12, 14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.version }}
Example: Using Contexts to Create Matrices
You can use contexts to create matrices. For more information about contexts, see "Contexts."
For example, the following workflow triggers on the repository_dispatch
event and uses information from the event
payload to build the matrix. When a repository dispatch event is created with a payload like the one below, the matrix
version
variable will have a value of [12, 14, 16]
. For more information about the repository_dispatch
trigger,
see "Events that trigger workflows."
{
"event_type": "test",
"client_payload": {
"versions": [12, 14, 16]
}
}
on:
repository_dispatch:
types:
- test
jobs:
example_matrix:
runs-on: ubuntu-latest
strategy:
matrix:
version: ${{ github.event.client_payload.versions }}
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.version }}
Expanding or adding matrix configurations
Use jobs.<job_id>.strategy.matrix.include
to expand existing matrix configurations or to add new configurations. The
value of include is a list of objects.
For each object in the include
list, the key:value pairs in the object will be added to each of the matrix
combinations if none of the key:value pairs overwrite any of the original matrix values. If the object cannot be added
to any of the matrix combinations, a new matrix combination will be created instead. Note that the original matrix
values will not be overwritten, but added matrix values can be overwritten.
For example, this matrix:
strategy:
matrix:
fruit: [apple, pear]
animal: [cat, dog]
include:
- color: green
- color: pink
animal: cat
- fruit: apple
shape: circle
- fruit: banana
- fruit: banana
animal: cat
will result in six jobs with the following matrix combinations:
{fruit: apple, animal: cat, color: pink, shape: circle}
{fruit: apple, animal: dog, color: green, shape: circle}
{fruit: pear, animal: cat, color: pink}
{fruit: pear, animal: dog, color: green}
{fruit: banana}
{fruit: banana, animal: cat}
following this logic:
{color: green}
is added to all of the original matrix combinations because it can be added without overwriting any part of the original combinations.{color: pink, animal: cat}
addscolor:pink
only to the original matrix combinations that includeanimal: cat
. This overwrites the color: green that was added by the previous include entry.{fruit: apple, shape: circle}
adds shape: circle only to the original matrix combinations that includefruit: apple
.{fruit: banana}
cannot be added to any original matrix combination without overwriting a value, so it is added as an additional matrix combination.{fruit: banana, animal: cat}
cannot be added to any original matrix combination without overwriting a value, so it is added as an additional matrix combination. It does not add to the{fruit: banana}
matrix combination because that combination was not one of the original matrix combinations.
This looks like kinda "what're we doing here???". But let us just look at the following examples which make it more clear.
The following workflow will run six jobs, one for each combination of os
and node. When the job for the os value of
windows-latest
and node value of 16
runs, an additional variable called npm
with the value of 6
will be included
in the job.
jobs:
example_matrix:
strategy:
matrix:
os: [windows-latest, ubuntu-latest]
node: [12, 14, 16]
include:
- os: windows-latest
node: 16
npm: 6
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- if: ${{ matrix.npm }}
run: npm install -g npm@${{ matrix.npm }}
- run: npm --version
This matrix will run 10 jobs, one for each combination of os
and version
in the matrix, plus a job for the os
value of windows-latest
and version value of 17
.
jobs:
example_matrix:
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
version: [12, 14, 16]
include:
- os: windows-latest
version: 17
If you don't specify any matrix variables, all configurations under include
will run. For example, the following
workflow would run two jobs, one for each include
entry. This lets you take advantage of the matrix strategy without
having a fully populated matrix.
jobs:
includes_only:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- site: "production"
datacenter: "site-a"
- site: "staging"
datacenter: "site-b"
Excluding Matrix Configurations
To remove specific configurations defined in the matrix, use jobs.<job_id>.strategy.matrix.exclude
. An excluded
configuration only has to be a partial match for it to be excluded. For example, the following workflow will run nine
jobs: one job for each of the 12 configurations, minus the one excluded job that matches
{os: macos-latest, version: 12, environment: production}
, and the two excluded jobs that match
{os: windows-latest, version: 16}
.
strategy:
matrix:
os: [macos-latest, windows-latest]
version: [12, 14, 16]
environment: [staging, production]
exclude:
- os: macos-latest
version: 12
environment: production
- os: windows-latest
version: 16
runs-on: ${{ matrix.os }}
Note: All
include
combinations are processed afterexclude
. This allows you to useinclude
to add back combinations that were previously excluded.
Handling Failures
You can control how job failures are handled with jobs.<job_id>.strategy.fail-fast
and
jobs.<job_id>.continue-on-error
.
jobs.<job_id>.strategy.fail-fast
applies to the entire matrix. If jobs.<job_id>.strategy.fail-fast
is set to true
,
GitHub will cancel all in-progress and queued jobs in the matrix if any job in the matrix fails. This property defaults
to true
.
jobs.<job_id>.continue-on-error
applies to a single job. If jobs.<job_id>.continue-on-error
is true
, other jobs in
the matrix will continue running even if the job with jobs.<job_id>.continue-on-error: true
fails.
You can use jobs.<job_id>.strategy.fail-fast
and jobs.<job_id>.continue-on-error
together. For example, the
following workflow will start four jobs. For each job, continue-on-error is determined by the value of
matrix.experimental
. If any of the jobs with continue-on-error: false
fail, all jobs that are in progress or queued
will be cancelled. If the job with continue-on-error: true
fails, the other jobs will not be affected.
jobs:
test:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: true
matrix:
version: [6, 7, 8]
experimental: [false]
include:
- version: 9
experimental: true
Configuring the Maximum Number of Concurrent Jobs
By default, GitHub will maximize the number of jobs run in parallel depending on runner availability. To set the maximum
number of jobs that can run simultaneously when using a matrix
job strategy, use
jobs.<job_id>.strategy.max-parallel
.
For example, the following workflow will run a maximum of two jobs at a time, even if there are runners available to run all six jobs at once.
jobs:
example_matrix:
strategy:
max-parallel: 2
matrix:
version: [10, 12, 14]
os: [ubuntu-latest, windows-latest]