Menu Octopus Deploy

GitHub Actions Matrix strategy: Basics, tutorial & best practices

What is GitHub Actions matrix?

The GitHub Actions matrix allows developers to automate testing and deployment processes across various configurations, within the GitHub Actions CI/CD platform. It provides a structured way to define multiple parallel job executions in a single GitHub Actions workflow, by specifying different environments or combinations. This approach ensures code testing, reducing unexpected failures in production.GitHub Actions matrix operates through a declarative syntax, enabling management of variable combinations—such as different operating systems, Node.js versions, or Python environments. This enables simultaneous execution of tasks across predefined scenarios. It promotes more efficient continuous integration processes, enabling rapid feedback on platform compatibilities without manual intervention to modify workflow scripts.

Here is a simple example of a matrix that tests three application versions across three types of platforms, running nine jobs in a single workflow:

jobs:
  example_matrix:
    strategy:
      matrix:
        platform: [desktop, mobile, iot]
        app-version: [1.3, 1.5, 1.8]

Example workflow

Benefits of using matrix strategies

The main benefit of using matrix strategies in GitHub Actions is increased coverage in testing code under various conditions. This is achieved by automating multiple configurations and environments within a single workflow, ensuring that code remains functional across different scenarios. The tests identify compatibility issues early in the development cycle, reducing the likelihood of bugs in production environments.

Matrix strategies also improve continuous integration practices by executing numerous job combinations simultaneously. This parallel execution reduces runtime, allowing developers to receive test feedback more swiftly. Additionally, by setting matrix parameters, teams can easily expand their testing suite to incorporate new environments as they develop.

Quick tutorial: Using GitHub Actions Matrix

Here’s an overview of how to use and configure a matrix in GitHub Actions. These instructions are adapted from the GitHub Actions documentation.

Basic matrix strategy

The jobs.<job_id>.strategy.matrix section is where the matrix is configured, using variables with lists of possible values. Each combination of these variables will create a separate job.

Here’s a simple example that defines two variables: version with values [13, 15, 17] and os with values [ubuntu-latest, windows-latest].

jobs:
  matrix_example:
strategy:  
  matrix:  
    version: [13, 15, 17]  
    os: [ubuntu-latest, windows-latest]  
runs-on: $  
steps:  
  - name: Setup Node.js  
    uses: actions/setup-node@v4  
    with:  
      node-version: $  
  - run: node --version

This configuration will run six jobs, covering each combination of version and os. In each job, matrix.version and matrix.os will reflect the current values, making it easy to adjust the setup or steps for each environment.

Expanding or adding matrix configurations

In some cases, you may need specific job configurations that aren’t covered by the main matrix. The include keyword allows you to add additional key-value pairs to specific matrix combinations or introduce entirely new configurations.

Consider the following example, where we add an npm version to a combination of os and node:

jobs:
  matrix_example:
strategy:  
  matrix:  
    os: [ubuntu-latest, windows-latest]  
    node: [13, 17]  
    include:  
      - os: windows-latest  
        node: 17  
        npm: 7  
runs-on: $  
steps:  
  - uses: actions/setup-node@v4  
    with:  
      node-version: $  
  - if: $  
    run: npm install -g npm@$  
  - run: npm --version

This configuration creates four jobs, one for each combination of os and node, but only the job for windows-latest with Node.js 13 will have npm set to 6.

Excluding matrix configurations

If you want to avoid running specific combinations, use the exclude keyword. This is useful for skipping unnecessary configurations or known incompatible setups. For example, here’s how to exclude selected combinations:

strategy:
  matrix:
os: [macos-latest, windows-latest]  
version: [16, 17, 18]  
environment: [staging, production]  
exclude:  
  - os: macos-latest  
    version: 16  
    environment: production  
  - os: windows-latest  
    version: 18

In this setup, the matrix initially creates jobs for all combinations of os, version, and environment. However, two configurations are excluded: macos-latest with version 16 in production, and windows-latest with version 18, effectively reducing the total number of jobs run.

Using an output to define two matrices

In GitHub Actions, outputs from one job can be used to define matrices for multiple jobs. This is useful when you need to dynamically generate values for a matrix based on previous job results. In this example, we define colors in an initial job, use them to create artifacts in a second job, and then access these artifacts in a third job.

name: Shared Matrix Example
on:
  push:
  workflow_dispatch:

jobs:
  define-matrix:
    runs-on: ubuntu-latest
    outputs:
      colors: ${{ steps.colors.outputs.colors }}
    steps:
      - name: Define Colors
        id: colors
        run: |
          echo 'colors=["yellow", "orange", "brown"]' >> "$GITHUB_OUTPUT"

  produce-artifacts:
    runs-on: ubuntu-latest
    needs: define-matrix
    strategy:
      matrix:
        color: ${{ fromJson(needs.define-matrix.outputs.colors) }}
    steps:
      - name: Define Color
        env:
          color: ${{ matrix.color }}
        run: echo "$color" > color
      - name: Produce Artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.color }}
          path: color

  consume-artifacts:
    runs-on: ubuntu-latest
    needs:
      - define-matrix
      - produce-artifacts
    strategy:
      matrix:
        color: ${{ fromJson(needs.define-matrix.outputs.colors) }}
    steps:
      - name: Retrieve Artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ matrix.color }}
      - name: Report Color
        run: cat color

In this setup:

  1. Define colors: The define-matrix job outputs an array of colors.
  2. Produce artifacts: The produce-artifacts job uses each color from the matrix to create an artifact named after the color.
  3. Consume artifacts: Finally, consume-artifacts downloads and reads each color artifact, providing flexibility for each color to be processed independently.

Handling failures

GitHub Actions offers controls to handle matrix failures using fail-fast and continue-on-error:

  • fail-fast: If set to true, all in-progress and queued jobs are canceled if any job in the matrix fails. This is enabled by default, allowing quick termination of jobs upon failure to save time and resources.
  • continue-on-error: This applies to individual jobs. When set to true, other jobs in the matrix continue to run even if the job fails, making it useful for experimental tests where errors can be ignored.

Here’s an example where fail-fast is enabled, and continue-on-error is set based on the experimental property in the matrix:

jobs:
  test:
runs-on: ubuntu-latest  
continue-on-error: $  
strategy:  
  fail-fast: true  
  matrix:  
    version: [9, 10, 11]  
    experimental: [false]  
    include:  
      - version: 12  
        experimental: true

In this workflow:

  • Jobs with experimental: [false] will halt other jobs if they fail.
  • Jobs with experimental: true will allow other jobs to continue even if they fail, offering flexibility in handling optional or test-only failures.

Best practices for using matrix strategies

Developers should consider the following practices when working with matrix strategies in GitHub Actions.

Optimize the number of combinations

Optimizing the number of combinations in matrix strategies is crucial for balancing thorough testing with resource and time efficiency. To achieve this, developers should prioritize configurations representing the most critical environments or those most likely to yield valuable results. This targeted approach minimizes redundant executions.

Prioritization might involve analyzing historical data to identify frequently failing environments or leveraging statistical techniques to focus on configurations with the highest impact on application performance. By methodically reducing excessive combinations, developers can maintain coverage while avoiding unnecessary resource consumption.

Keep workflows efficient

To keep workflows efficient when using matrix strategies, developers must simplify both job configurations and execution flow. This involves reducing task complexity, reusing common actions, and eliminating superfluous steps. Additionally, leveraging caching strategies to minimize redundant work, such as dependency installation, reduces execution time.

An efficient workflow ensures each job is only as complex as necessary, focusing on critical testing and build criteria. Developers should regularly review and refactor their configurations to remove or optimize sections that cause bottlenecks. This accelerates pipeline throughput and improves maintainability, making workflows easier to adapt to evolving project requirements.

Secure the Workflow with Proper Permissions

Securing GitHub Actions workflows involves setting proper permissions to minimize the risk of unauthorized access or malicious activities within the CI/CD pipeline. By configuring the permissions keyword in workflows, developers can explicitly define the least privilege principles required for each job execution, making sure only necessary scopes are granted.

Additionally, it’s recommended to use token permissions judiciously and avoid hardcoding sensitive information in workflow files. Using GitHub secrets for sensitive data management further increases security by ensuring credentials are stored and accessed securely.

Use Self-Hosted Runners Wisely

Self-hosted runners in GitHub Actions offer increased control over environments and resources for job execution. However, they also introduce considerations for network access, maintenance, and security. Developers should ensure these runners are properly maintained, with regular updates and access controls, to mitigate risks associated with running jobs outside GitHub’s hosted infrastructure.

Choosing self-hosted runners can improve performance, especially for jobs requiring hardware or software not available on GitHub-hosted runners. However, it’s crucial to balance the benefits against operational overheads. By wisely deploying self-hosted runners—prioritizing critical workflows—developers can extend their CI/CD capabilities effectively.

Document matrix configurations

Documenting matrix configurations in GitHub Actions involves maintaining clear, updated records of workflow YAML structures, variable definitions, and key decisions. This ensures that anyone working on the project can understand the purpose and logic behind different testing strategies, aiding in troubleshooting, maintenance, and future development.

Effective documentation includes descriptive comments within YAML files and separate documentation resources outlining the context and reasoning for matrix setups. Such documentation supports team collaboration and onboarding, ensuring that workflow details are transparent and modifications are made in an informed manner.

Help us continuously improve

Please let us know if you have any feedback about this page.

Send feedback

Categories:

Next article
GitHub Actions Reusable Workflows