Last updated 2026-05-07

Matrix & Reusable Workflows

Matrix strategies

Full support: cartesian product over keys, plus include additions and exclude removals.

strategy:
matrix:
node: [18, 20, 22]
os: [ubuntu-latest, macos-latest]
include:
- node: 18
experimental: true
exclude:
- node: 22
os: ubuntu-latest
fail-fast: true # default — cancels remaining on first failure
max-parallel: 2 # at most 2 matrix instances run concurrently

How the runner expands this:

  1. Compute the cartesian product of the matrix keys.
  2. Remove every combination that matches any exclude entry.
  3. For each include, merge into a matching combination if all overlapping keys agree, otherwise add as a new instance.
  4. Apply max-parallel at scheduling time.
  5. If any instance fails and fail-fast is true, cancel the rest.

Sibling output

jobs:
test:
strategy:
matrix:
node: [18, 20]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm test

Reusable workflows

Both local (./) and remote (org/repo@ref) reusable workflows are supported.

Calling a local reusable workflow

.github/workflows/main.yml
jobs:
tests:
uses: ./.github/workflows/shared-tests.yml
with:
environment: staging
secrets: inherit
.github/workflows/shared-tests.yml
on:
workflow_call:
inputs:
environment:
type: string
required: true
secrets:
API_KEY:
required: false
jobs:
run-tests:
runs-on: ubuntu-latest
steps:
- run: echo "Testing in ${{ inputs.environment }}"
- run: ./scripts/test.sh
env:
API_KEY: ${{ secrets.API_KEY }}

Calling a remote reusable workflow

jobs:
external:
uses: org/repo/.github/workflows/shared.yml@main
with:
param: value
secrets:
API_KEY: ${{ secrets.API_KEY }}

Remote workflows are git-cloned and cached at ~/.local/share/local-runner/workflow-cache/. Authentication for private repos uses GITHUB_TOKEN / GH_TOKEN from the runner environment.

Secrets handling

  • secrets: inherit — every secret in the calling workflow is forwarded to the called workflow.
  • secrets: { KEY: value } — explicit forwarding, one secret at a time, evaluated through the expression engine.
  • A called workflow's workflow_call.secrets block declares which secrets it expects.

Job dependencies

Dependencies are resolved by topological sort with cycle detection. Failure on a dependency makes downstream jobs skip (unless they use if: always() or if: failure()).

jobs:
build:
runs-on: ubuntu-latest
steps: [{ run: echo build }]
test:
needs: build
runs-on: ubuntu-latest
steps: [{ run: echo test }]
deploy:
needs: [build, test]
if: success() && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps: [{ run: ./deploy.sh }]