GitHub Actions Matrix Generator
Build a GitHub Actions matrix workflow with runner and runtime axes, cache choices, exception rows, and review checks before committing.{{ workflowYaml }}
| Runner OS | {{ versionAxisLabel }} | Extra axis | Source | Experimental | Copy |
|---|---|---|---|---|---|
| {{ row.os }} | {{ row.version }} | {{ row.extraAxis }} | {{ row.source }} | {{ row.experimental ? 'yes' : 'no' }} |
| Check | Status | Detail | Copy |
|---|---|---|---|
| {{ row.check }} | {{ row.status }} | {{ row.detail }} |
Introduction:
A continuous integration matrix is a compact way to ask the same build or test question across several supported environments. Instead of copying one job for Node.js 20, another for Node.js 22, and another for macOS, the workflow defines axes such as runtime version and runner operating system. GitHub Actions expands those axes into job runs, then exposes the chosen values through the matrix context for setup steps, cache keys, and commands.
The hard part is deciding what deserves to be in the grid. A larger matrix catches more compatibility drift, but it also consumes runner minutes, slows pull request feedback, and creates more failure messages for reviewers to triage. Good matrix design starts from a real support promise: a library may need every maintained runtime, a CLI may need each operating system it ships to, and an application may only need the production runtime plus one next-version smoke job.
- Matrix axis
- A named list of values, such as runner labels, runtime versions, package managers, databases, architectures, or shards.
- Matrix combination
- One job created from a specific value on every active axis.
- Exception row
- An include or exclude entry used when the regular grid has an unsupported pair, an experimental case, or another special rule.
Most workflows begin with two axes: runner label and runtime version. A third axis is useful only when it changes the evidence, for example package manager, database version, architecture, or shard number. It is easy to add a dimension that looks thorough but only multiplies duplicate failures. Moving runner labels such as ubuntu-latest also need attention because they follow GitHub-hosted runner image updates, which can change tools and system packages over time.
Exception rows should stay rare and easy to explain in code review. Exclude rows remove combinations that are unsupported, redundant, or intentionally out of scope. Include rows fit deliberate additions such as a prerelease runtime, an experimental package manager, or a one-off compatibility job that should not be confused with the stable grid.
A passing matrix still proves only the combinations it ran. The workflow depends on valid setup actions, available runner labels, real dependency files, appropriate cache paths, and commands that behave the same way in continuous integration as they do in a local checkout.
How to Use This Tool:
Build the ordinary CI workflow first, then add advanced settings only when the repository needs a named exception, a third axis, or a different trigger policy.
- Choose
Runtime presetand check the filledRuntime label,Setup action,Version input name,Install command, andTest command. Switch to a custom setup only when the preset does not match the repository language or action version. - Enter
Runtime versionsandRunner OS labelswith one value per line or comma-separated values. Watch the summary job count before copying the workflow, especially after adding a third axis. - Set
Workflow name. OpenAdvancedwhen the repository needs a differentWorkflow file,Job id,Push branches,Working directory, or read-only contents permission block. - Choose
Dependency cache. Resolve warnings aboutCache package/input,Dependency path, orManual cache pathbefore treating cache behavior as ready for branch protection. - Use the extra axis fields only for a real compatibility dimension such as package manager, database, architecture, or shard. Values without an axis name block output until you either name the axis or clear the values.
- Add include and exclude rows as
key=valuelines for exceptions. Complete include rows are easier to audit because theMatrix Ledgerrecords entered include rows as explicit additions. - Set
Fail-fast,Max parallel,Timeout, and the trigger switches. If push, pull request, and manual triggers are all off,Workflow YAMLremains empty until one trigger is enabled.
Interpreting Results:
GitHub Actions matrix ready means the entered settings produced workflow text and review artifacts. Start with the headline job count because it shows how much CI work each workflow run can create before GitHub executes anything.
Workflow YAML is the content to copy or download. Matrix Ledger shows the local planning rows, including base rows and entered include rows. Workflow Checks is the safety readout: Blocked rows prevent YAML output, while Review rows flag choices that may be valid but still need a repository-specific decision.
- An
OKsetup or cache check does not prove that the repository contains the named lockfile, package manager, runner label, or runtime version. Commit the workflow and run it manually before making the check required. Matrix combinationsshould stay at or below256jobs. A larger count needs fewer axis values, narrower exceptions, or a split workflow.Fail-fast policymarkedReviewmeans one required failure can cancel queued or in-progress matrix jobs, which may hide other compatibility failures.Dependency cachemarkedReviewmeans no cache is emitted or a manual cache path is missing. That affects speed and reproducibility checks, not the basic ability to write a workflow.
Technical Details:
A matrix strategy expands one job definition into job runs before any step executes. Each axis becomes a property in the matrix context, so a runtime version can feed a setup action, a runner label can drive runs-on, and the same value can appear in job names or cache keys.
GitHub documents a maximum of 256 matrix jobs per workflow run, for both GitHub-hosted and self-hosted runners. Parallelism is otherwise limited by runner availability unless max-parallel is set. Job timeouts use whole minutes, and this generator clamps entered timeout values to 1 through 360 minutes to match the common workflow syntax boundary.
Formula Core:
For the generated ledger, the local planning count is the base Cartesian product minus matching excludes plus entered include rows.
O is the number of runner OS labels, V is the runtime version count, and A is the extra-axis count or 1 when no extra axis is used. E is the number of base combinations removed by exclude rows, and I is the number of include rows counted as added ledger rows. With 3 runner labels, 4 runtime versions, and no extra axis, the base grid is 12 jobs. Two matching excludes and one entered include row produce 11 local planning rows.
Rule Core:
| Part | Rule | Review point |
|---|---|---|
strategy.matrix |
Runner labels, runtime versions, and an optional extra axis become YAML lists. | Every added axis multiplies the base job count. |
exclude |
A row removes base combinations whose keys all match the row values. | Partial rows can remove more combinations than expected when they omit a version or runner label. |
include |
Entered rows are emitted under strategy.matrix.include and counted locally as added ledger rows. |
Use complete exception rows because GitHub can merge incomplete include entries into existing combinations when they do not overwrite matrix values. |
fail-fast |
The strategy writes either true or false. |
false keeps the full matrix running after a required job fails. |
max-parallel |
0 omits the setting. A value from 1 to 256 caps concurrent matrix jobs. |
A cap can protect limited runners, external services, and rate-limited test dependencies. |
timeout-minutes |
The job timeout is clamped to a whole number from 1 to 360 minutes. |
A very low timeout can fail slow-but-valid combinations before they reveal useful evidence. |
Workflow Assembly:
The generated YAML uses pull request, push, and manual triggers according to the selected switches. Push branches are emitted only when the push trigger is enabled and branch names are present. Read-only contents permission is included when that switch is on, and a working-directory default is emitted only when a non-root directory is entered.
Setup-action caching and manual caching solve different problems. Setup-action cache inputs are concise when the language setup action supports the package manager. Manual cache steps expose the cache path directly and build a key from the runner OS, runtime version, and dependency-path hash expression. Generic lockfile globs are a fallback in either path, not a substitute for checking the real repository layout.
| Output | What it contains | Boundary |
|---|---|---|
Workflow YAML |
Triggers, permissions, defaults, one matrix job, checkout, setup, optional cache, install, and test steps. | It is not pushed to a repository or validated by GitHub until you commit and run it. |
Matrix Ledger |
Runner OS, runtime version, optional extra axis, source, and experimental flag for each planned row. | A planned row is intended coverage, not a completed GitHub Actions job. |
Workflow Checks |
OK, Review, or Blocked status for path, count, setup, cache, triggers, fail-fast, timeout, warnings, and errors. |
OK means the local generation rules passed, not that the workflow is accepted by GitHub. |
JSON |
The same parameters, matrix rows, checks, warnings, errors, and generated YAML in structured form. | Use it for review or recordkeeping, not as a GitHub API response. |
Accuracy and Privacy Notes:
The generated workflow is assembled from visible settings and local review rules. It does not inspect repository files, check current runner images, confirm that setup-action versions exist, or ask GitHub to validate the YAML.
- Run the committed workflow manually before making it a branch protection requirement.
- Confirm setup-action versions, supported cache inputs, and dependency file paths against the current action documentation for the repository.
- Workflow content is not sent to GitHub by the generator. Copy and download actions use the current browser output.
Worked Examples:
Default Node.js CI
The Node.js preset with versions 20, 22, and 24 across ubuntu-latest and macos-latest creates 6 jobs. Workflow path shows .github/workflows/ci.yml, Workflow YAML sets the selected Node version from the matrix, and Matrix Ledger lists six base rows.
Python with one unsupported runner
A Python matrix using 3.11, 3.12, and 3.13 on ubuntu-latest and windows-latest starts with six jobs. Adding os=windows-latest,python-version=3.13 to Matrix exclude rows removes that unsupported combination, so Matrix Ledger should show five rows and the YAML should include an exclude entry.
Oversized compatibility grid
Six runner labels, eight runtime versions, and six shard values expand to 288 jobs before exceptions. Workflow Checks marks Matrix combinations as Review because the count is over GitHub's 256-job limit. Reduce an axis, add matching exclude rows, or split the coverage across more than one workflow.
Experimental include row
A stable Node.js matrix can add os=ubuntu-latest,node-version=25,experimental=true under Matrix include rows. The added Matrix Ledger row is marked yes for experimental, and Workflow YAML includes continue-on-error: ${{ matrix.experimental == true }} so that experimental failure does not fail the workflow run by itself.
No YAML after trigger changes
If Push trigger, Pull request trigger, and Manual trigger are all off, the error list says Enable at least one trigger. and Workflow YAML stays empty. Turn on Manual trigger for a safe first run, then verify that Blocking errors returns to OK.
FAQ:
Does the generator run the workflow in GitHub?
No. It creates Workflow YAML, Matrix Ledger, Workflow Checks, and JSON from the entered values. Commit the YAML and run it in GitHub Actions to confirm repository-specific behavior.
Why is the YAML output empty?
A blocking error is present. Common causes are empty Runtime versions, empty Runner OS labels, missing install or test commands, no enabled trigger, or extra-axis values without an axis name.
Should include rows contain every matrix key?
Yes when you want predictable review. Complete include rows make the added Matrix Ledger entry clear and avoid GitHub's merge-style include behavior changing how the row expands.
Why are version numbers quoted in the YAML?
Runtime versions are emitted as strings so values such as 3.10, 20, or lts/* stay version labels instead of being treated as ordinary YAML numbers.
What does the cache warning mean?
A cache warning means the generated cache step may be incomplete or broad. Check Cache package/input, Dependency path, and Manual cache path against the repository before relying on faster CI runs.
Does the generator send workflow content to GitHub?
No. It does not call the GitHub API or push files to a repository. The workflow text remains in the browser until you copy, download, commit, and run it yourself.
Glossary:
- Matrix axis
- A named list of values that GitHub combines with other axes, such as
osor a runtime version key. - Matrix combination
- One planned job created from a specific set of axis values.
- Runner label
- The
runs-onvalue that selects the GitHub-hosted or self-hosted runner environment. - Setup action
- An action that prepares a language runtime before install and test commands run.
- Include row
- An additional matrix entry emitted under
strategy.matrix.include. - Exclude row
- A key-value rule that removes matching base matrix combinations.
- Fail-fast
- A matrix policy that can cancel queued or in-progress jobs after a required matrix job fails.
- Workflow trigger
- An event such as
push,pull_request, orworkflow_dispatchthat starts a workflow run.
References:
- Workflow syntax for GitHub Actions, GitHub Docs.
- Running variations of jobs in a workflow, GitHub Docs.
- Dependency caching reference, GitHub Docs.