I came across GitHub Actions matrix strategies during a failed attempt in trying to derive a self-hosted runner label from workflow environment variables.
Below is a sample of the workflow that failed to run.
name: Workflow will fail
on:
push:
env:
runner_os: ubuntu
runner_arch: amd64
runner_size : large
jobs:
build:
runs-on: ${{ env.runner_os }}-${{ env.runner_arch }}-${{ env.runner_size }}
steps:
- name: run basic test
run: |
echo ${{ env.runner_os }}
echo ${{ env.runner_arch }}
echo ${{ env.runner_size }}
I was expecting:
runs-on: ${{ env.runner_os }}-${{ env.runner_arch }}-${{ env.runner_size }}
to resolve to the following:
runs-on: ubuntu-amd64-large
Instead, the workflow failed:

Turns out that runs-on:
will only resolve expressions referencing the following contexts:
github, inputs, vars, needs, strategy, matrix
Working within the constraints of the current development project, the only feasible option was to go with: strategy, matrix
.
To resolve the issue, the workflow was modified to reference variables defined at the the strategy, matrix
level.
name: Workflow will pass
on:
push:
jobs:
build:
strategy:
matrix:
include:
- runner_os: ubuntu
runner_arch: amd64
runner_size: large
runs-on: ${{ matrix.runner_os }}-${{ matrix.runner_arch }}-${{ matrix.runner_size }}
steps:
- name: run basic test
run: |
echo ${{ matrix.runner_os }}
echo ${{ matrix.runner_arch }}
echo ${{ matrix.runner_size }}
The workflow resolved the runner label as expected.

This particular use-case of strategy/matrix could be considered as “off-label”, and fails to elaborate on additional benefits of using strategy/matrix within workflows: job parallelism and concurrency.
Expand on Learnings: Creating a Basic Multi-dimensional Matrix
Take the case where you wish to concurrently deploy an AWS app to two different accounts, i.e. SIT and UAT.
The app and account details are:
Parameter | SIT | UAT |
---|---|---|
Application name | new-app | new-app |
AWS Account ID | 123456789012 | 234567890123 |
ECS Cluster name | cluster-system-integration-test | cluster-user-acceptance-test |
Using a multi-dimension matrix, the sample template workflow below would deploy the app in parallel to the target AWS accounts:
name: Multi-dimension matrix sample
on:
push:
jobs:
deploy_to_aws:
strategy:
matrix:
deploy_to: [SIT, UAT]
app_name: [new-app]
include:
- deploy_to: SIT
aws_acct_id: 123456789012
cluster_name: cluster-system-integration-test
- deploy_to: UAT
aws_acct_id: 234567890123
cluster_name: cluster-user-acceptance-test
runs-on: ubuntu-latest
steps:
- name: "Checkout"
uses: actions/checkout@v4
- name:
run: |
echo "Deploying application: ${{ matrix.app_name }} to target environment: ${{ matrix.deploy_to }}"
echo "Target AWS Account id: ${{ matrix.aws_acct_id }}"
echo "Target Cluster name: ${{ matrix.cluster_name }}"
The workflow run would generate the following jobs:
{deploy_to: SIT, app_name: new-app}
- the matrix’ include attribute allows expansion of the SIT configuration so that it includes elements which are specific to SIT context, i.e:
aws_acct_id: 123456789012
cluster_name: cluster-system-integration-test
- the matrix’ include attribute allows expansion of the SIT configuration so that it includes elements which are specific to SIT context, i.e:
{deploy_to: UAT, app_name: new-app}
- again, the configuration is expanded through the include by adding
aws_acct_id: 234567890123
cluster_name: cluster-user-acceptance-test
- again, the configuration is expanded through the include by adding
Running the workflow produces the following output:
