The matrix strategy in GitHub Actions
Understand the matrix strategy in GitHub Actions and how to use it to run jobs across multiple configurations.
The matrix strategy â runs jobs across multiple configurations - different OS versions, language versions, or other parameters. This tests your application across environments without duplicating workflows.
How the matrix strategy works
Section titled âHow the matrix strategy worksâDefine configurations in the strategy block using the matrix key. GitHub Actions automatically creates and runs a job for each combination.
Benefits of using a matrix strategy
Section titled âBenefits of using a matrix strategyâ- Efficiency: Automatically generates test runs across multiple configurations
- Simplicity: Reduces redundancy and keeps workflows maintainable
- Coverage: Tests across multiple environments, architectures, and software versions
Example: compiling across multiple architectures using docker
Section titled âExample: compiling across multiple architectures using dockerâThis example builds and pushes Docker images for multiple architectures (amd64, arm64) using GitHub Actions.
Workflow setup
Section titled âWorkflow setupâUsing docker/build-push-action â with a matrix strategy:
name: Build and push Docker images
on: push: branches: - main
jobs: build: runs-on: ubuntu-latest strategy: matrix: platform: - linux/arm64/v8 - linux/amd64
steps: - name: Checkout code uses: actions/checkout@v6
- name: Set up QEMU uses: docker/setup-qemu-action@v3
- name: Set up Docker buildx id: buildx uses: docker/setup-buildx-action@v3
- name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push uses: docker/build-push-action@v4 with: context: . file: Dockerfile push: true tags: REPO/APP:MY_TAG platforms: ${{ matrix.platform }}Explanation of the workflow
Section titled âExplanation of the workflowâ- Trigger: The workflow triggers on a push to the
mainbranch. - Matrix strategy: Defines a matrix of platforms to build a docker image for (
linux/amd64andlinux/arm64/v8). GitHub Actions will create two jobs, one for each architecture. - Docker actions:
- Set up Docker buildx: Prepares the Docker buildx builder, which is a part of Docker CLI starting from 19.03 and supports building multi-architecture images.
- Login to DockerHub: Logs into DockerHub to allow pushing the images.
- Build and push: Builds the Docker image and pushes it to DockerHub.
This setup ensures that your Docker images are built for both linux/amd64 and linux/arm64/v8 architectures automatically whenever changes are pushed to the main branch.
Advanced features of the matrix strategy
Section titled âAdvanced features of the matrix strategyâExcluding configurations
Section titled âExcluding configurationsâIn complex workflows, you might not need to run a job for every possible combination in a matrix. GitHub Actions allows you to explicitly exclude certain configurations using the exclude keyword. This is particularly useful when certain combinations are known to be incompatible or unnecessary.
strategy: matrix: os: [ubuntu-latest, windows-latest] node: [18, 20] exclude: - os: ubuntu-latest node: 18In this example, the job will run for all combinations except for Ubuntu with Node 18.
Including additional configurations
Section titled âIncluding additional configurationsâConversely, you can include additional configurations that are not part of the standard matrix combinations using the include keyword. This allows for testing against specific versions or settings without creating a completely separate workflow.
strategy: matrix: os: [ubuntu-latest, windows-latest] node: [18, 20] include: - os: macos-latest node: 20 additional: "experimental feature"Here, an additional job for macOS with Node 20 and an experimental feature is added to the matrix.
Dynamic matrix generation
Section titled âDynamic matrix generationâFor projects that require a dynamic configuration, you can generate the matrix based on the output of a previous step or job. This is achieved by setting the matrix via expressions and using outputs from previous steps.
jobs: build: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - id: set-matrix run: | echo "matrix={\"include\": [{\"os\": \"ubuntu-latest\", \"node\": \"20\"}]}" >> "$GITHUB_OUTPUT"
test: needs: build strategy: matrix: ${{fromJson(needs.build.outputs.matrix)}} runs-on: ${{ matrix.os }} steps: - run: npm testThis setup dynamically creates a matrix for the test job based on the output from the build job.
Fail-fast behavior
Section titled âFail-fast behaviorâBy default, GitHub Actions uses a fail-fast strategy for matrix jobs. This means if one job in the matrix fails, all other jobs that are still running will be cancelled. This can save time and resources when an issue is detected early in one of the configurations. However, you might want to disable this behavior if you need results from all matrix jobs, regardless of individual failures.
strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] node: [18, 20]Setting fail-fast to false ensures that all jobs complete, providing a full set of results for analysis.
Max parallel jobs
Section titled âMax parallel jobsâTo manage resource consumption and possibly improve build times if using self-hosted runners, you can limit the number of jobs that run in parallel using the max-parallel key. This is particularly useful when you have a large matrix and a limited number of runners available.
strategy: max-parallel: 2 matrix: os: [ubuntu-latest, windows-latest] node: [14, 16, 18, 20]In this configuration, no more than two jobs will run at the same time, even if more runners are available. This helps in managing the load on the available infrastructure and can be adjusted based on the specific needs of the project or the CI environment.
Using the matrix context and strategy.job-index variable
Section titled âUsing the matrix context and strategy.job-index variableâGitHub Actions provides a matrix context that can be used within a job to access the current matrix configuration. Additionally, the strategy.job-index variable is available to identify the current jobâs index within the matrix strategy. This can be particularly useful for tasks that need to reference the position or order of the job.
jobs: test: strategy: matrix: os: [ubuntu-latest, windows-latest] node: [18, 20] runs-on: ${{ matrix.os }} steps: - name: Display matrix and index run: | echo "Running on ${{ matrix.os }} with Node.js ${{ matrix.node }}" echo "This is matrix job #${{ strategy.job-index }}"In this example, each job will output which operating system and Node.js version it is using, along with its index in the matrix. This index is zero-based, so the first job in the matrix will have an index of 0.
The strategy.job-index variable is especially useful for parallel testing scenarios where you might want to split tests evenly or assign specific tests to certain matrix jobs based on their index.