Skip to main content
Understanding Matrix Builds in GitHub Actions
5 min read

Understanding Matrix Builds in GitHub Actions

Introduction

GitHub Actions provides so-called matrix strategy for running jobs across multiple configurations. You might specify the parameters of jobs, like types of OS or different versions of programming languages your application supports, and a set of jobs can be automatically created with all possible combinations. It is a very effective manner to run multiple tests or builds in parallel so that your application works seamlessly in various environments without redundant configurations.

What is a Matrix Strategy?

The matrix strategy of GitHub Actions works on the concept of variable definitions that will be used in multiple job runs. For each defined combination, a new job is created; you can basically test or build your project across a variety of different platforms, versions, or settings in one sweep. It saves you from having duplicate workflow files for testing environments.

Example:

Suppose you want to test three versions of Node.js across two operating systems. Here's how you would create a matrix strategy:

jobs:
test-matrix:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
version: [10, 12, 14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.version }}
- run: npm test

In this example, GitHub Actions will automatically create for you six jobs, one for each combination of NodeJS version, 10, 12, and 14, and operating system, Ubuntu and Windows.

Single-Dimension Matrix Strategy

Another use is to define a single variable using a matrix when one only needs to test across different variants of one component, such as a programming language.

Example:

Setup a test to Run a Project with different Node.js versions:

jobs:
test-node-versions:
strategy:
matrix:
version: [10, 12, 14]
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.version }}
- run: npm test

In this example, three jobs are created-one for each Node.js version. This will help you make sure your code works as expected on all three versions.

Expanding Matrix Configurations

Sometimes you may want to customize or extend matrix configurations. You may want to add job conditions for some jobs, or add variables. For this reason, the matrix strategy supports two very powerful features: include and exclude.

Adding Custom Configurations with include

The include keyword allows specifying a job configuration that is not covered by the standard matrix. This is useful in order to test variables and combinations that are out of the standard pattern.

Example:

jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [14, 16]
include:
- os: windows-latest
node: 16
npm: 6
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- if: ${{ matrix.npm }}
run: npm install -g npm@${{ matrix.npm }}
- run: npm test

This is a configuration setup in addition to some basic matrix, where npm version 6 is only installed when running in Node.js 16 on Windows.

Excluding Unwanted Configurations with exclude

Sometimes certain settings may not apply or even conflict. You can use the exclude keyword in order to prevent the running of certain job configurations.

Example:

strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [14, 16, 18]
exclude:
- os: windows-latest
node: 18

Here, we exclude the matrix for the combination of windows and nodejs 18, so it will only run five jobs, and it will skip this particular combination.


Monitoring GitHub Actions Workflows

CICube is a GitHub Actions monitoring tool that provides you with detailed insights into your workflows to further optimize your CI/CD pipeline. With CICube, you will be able to track your workflow runs, understand where the bottlenecks are, and tease out the best from your build times. Go to cicube.io now and create a free account to better optimize your GitHub Actions workflows!

CICube GitHub Actions Workflow Duration Monitoring


Dynamic Matrices Techniques

Advanced workflows may want to dynamically build up matrices given the result of a previous step or job. Conveniently, this is useful when either dealing with completely unpredictable configurations or the need to adjust the matrix based on external factors.

Example:

jobs:
define-matrix:
outputs:
colors: ${{ steps.set-colors.outputs.colors }}
steps:
- name: Set Colors
id: set-colors
run: |
echo 'colors=["red", "green", "blue"]' >> "$GITHUB_OUTPUT"

use-matrix:
needs: define-matrix
strategy:
matrix:
color: ${{ fromJSON(needs.define-matrix.outputs.colors) }}
runs-on: ubuntu-latest
steps:
- run: echo "Current color: ${{ matrix.color }}"

For example, here the define-matrix job produces a list of colors as an output, that the second job then uses to define automatically a matrix, based on that output; this happens to be one of the most flexible and adaptive ways of creating workflows.

Handling Failures and Parallel Jobs

GitHub Actions also has a fail-fast option on matrix strategies where, if one of the jobs failed, it cancels the rest. This can save so much time and resources when something is wrong at the start of a complex build or deploy process. You can also disable the fail-fast behavior if you don't want it:.

Example:

strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
node: [14, 16, 18]

With the fail-fast flag set to false, all jobs will complete, and hence provide a full set of results to study.

Conclusion

GitHub Actions give you the capability to use matrix strategies. That way, you can run multiple jobs in parallel across different configurations. From being on different operating systems to variable programming languages and different environments-execution with matrix strategy saves you time, avoids redundancy, and ensures excellent test coverage. By including a feature like include, exclude, and even dynamically generating matrices, you will be able to create agile and robust workflows, well-suited for the particular needs of your project. This approach not only automatically simplifies running multiple tests or builds but also makes your CI/CD pipeline much more effective and reliable.