GitHub Actions Tutorial — Matrix Basics

2 minute read

Matrix Basics

A matrix strategy defines variables with lists of values. GitHub Actions creates a job for every combination. This is the foundation of cross-platform, multi-version testing.


Defining a Matrix


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

This creates 9 jobs (3 OS × 3 Node versions):

┌──────────────────────┬──────────────┬──────────────┬──────────────┐
│                      │ Node 18      │ Node 20      │ Node 22      │
├──────────────────────┼──────────────┼──────────────┼──────────────┤
│ ubuntu-latest        │ Job 1        │ Job 2        │ Job 3        │
│ windows-latest       │ Job 4        │ Job 5        │ Job 6        │
│ macos-latest         │ Job 7        │ Job 8        │ Job 9        │
└──────────────────────┴──────────────┴──────────────┴──────────────┘

All 9 jobs run in parallel (subject to concurrency limits).


How Matrix Variables Work

Matrix variables are available via the matrix context:


strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    python: ['3.10', '3.11', '3.12']
runs-on: ${{ matrix.os }}
steps:
  - uses: actions/setup-python@v5
    with:
      python-version: ${{ matrix.python }}
  - run: python --version

Important: Numeric values like 3.10 must be quoted as '3.10' — otherwise YAML parses it as 3.1 (a float).


Single-Dimension Matrix

A matrix can have just one variable:


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

This creates 4 parallel jobs, all on the same OS.


Matrix with Extra Variables

You can use matrix values anywhere — environment variables, artifact names, step logic:


strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    node: [18, 20]
runs-on: ${{ matrix.os }}
env:
  PLATFORM: ${{ matrix.os }}
steps:
  - uses: actions/setup-node@v4
    with:
      node-version: ${{ matrix.node }}
  - run: npm test
  - uses: actions/upload-artifact@v4
    with:
      name: results-${{ matrix.os }}-node${{ matrix.node }}
      path: ./test-results/

Artifact names must be unique per matrix combination — include matrix values in the name.


Matrix Limits

Limit Value
Max combinations 256 per workflow
Max concurrent jobs Depends on plan (20 for Free, 40 for Pro, 60 for Team)
Max dimensions No hard limit (but 256 total combinations)

Common Mistakes

Mistake Fix
YAML parses 3.10 as 3.1 Quote version strings: '3.10'
Artifact name collision Include matrix values in artifact name
Too many combinations Use include/exclude to prune (next topic)
Matrix in wrong location strategy: is at the job level, not step level

Quick Reference


jobs:
  my-job:
    strategy:
      matrix:
        dim1: [val1, val2]       # list of values
        dim2: [valA, valB]       # second dimension
    runs-on: ${{ matrix.dim1 }}  # use matrix values
    steps:
      - run: echo "${{ matrix.dim1 }}-${{ matrix.dim2 }}"

Concept Detail
Total jobs Product of all dimension sizes
Max combinations 256
Access values ${{ matrix.<variable> }}
Runs in Parallel (by default)

← Back to: Chapter 9: Matrix Builds

Next: Include & Exclude →

Updated: