Skip to content
Latchkey

How to Set a Matrix From Previous Job Output in GitHub Actions

When the set of things to test is computed at runtime, the matrix has to come from a previous jobs output.

Emit a JSON array to ${GITHUB_OUTPUT} in a setup job, expose it as a job output, then feed it to the matrix with fromJSON.

Steps

  • In a setup job, build the JSON array (e.g. from changed files).
  • Write matrix=<json> to ${GITHUB_OUTPUT} and map it to a job output.
  • In the downstream job add needs: setup.
  • Set matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}.

Workflow

.github/workflows/dynamic-matrix.yml
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.gen.outputs.matrix }}
    steps:
      - id: gen
        run: echo "matrix=[\"api\",\"web\",\"worker\"]" >> "${GITHUB_OUTPUT}"
  test:
    needs: setup
    runs-on: ubuntu-latest
    strategy:
      matrix:
        service: ${{ fromJSON(needs.setup.outputs.matrix) }}
    steps:
      - run: ./test.sh ${{ matrix.service }}

Gotchas

  • The output must be valid JSON; a stray trailing comma fails fromJSON silently as an empty matrix.
  • An empty array produces zero matrix jobs, which can look like a skipped step.
  • Latchkey scales these dynamic matrix legs onto cheaper runners and retries any leg that fails transiently.

Verify it actually works

A workflow that runs is not a workflow that works. Confirm the behaviour on a real event rather than on a manual dispatch, because trigger conditions, permissions, and context values all differ between the two.

Terminal
# 1. validate the file before pushing
docker run --rm -v "$(pwd):/repo" --workdir /repo rhysd/actionlint:latest -color

# 2. trigger the real event, not workflow_dispatch
git commit --allow-empty -m "ci: verify trigger" && git push

# 3. watch it and read the conclusion, not just the colour
gh run watch
gh run view --log-failed

What usually goes wrong first

  • The workflow file must exist on the default branch before scheduled or dispatch triggers appear at all.
  • GITHUB_TOKEN permissions default to read-only in many organisations. Declare a permissions: block listing every scope the job needs.
  • Fork pull requests get a read-only token and no access to secrets, regardless of workflow configuration.
  • actions/checkout gives you depth 1 on a detached HEAD, so anything needing history or a branch name needs fetch-depth: 0.

Frequently asked questions

How do I set a Matrix From Previous Job Output in GitHub Actions?
Emit a JSON array to ${GITHUB_OUTPUT} in a setup job, expose it as a job output, then feed it to the matrix with fromJSON.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card