Reusable workflows

GitHub Actions · Advanced · 12 min

Organizations with dozens of repositories need one maintained CI definition instead of copy-pasted YAML.

A reusable workflow is an ordinary workflow file whose trigger is on: workflow_call. Another workflow calls it at the job level, not as a step:

jobs:
  ci:
    uses: my-org/ci-templates/.github/workflows/node.yml@v3
    with: {node-version: 22}
    secrets: inherit

The callee declares typed inputs (string, number, boolean), the secrets it expects, and outputs mapped from its own jobs. The caller passes secrets explicitly or with secrets: inherit, which forwards all of the caller's secrets.

Things to know:

  • The called workflow's jobs appear in the caller's run, named caller-job / callee-job.
  • Calls can nest up to ten levels deep, and a run can involve at most 50 unique reusable workflows.
  • env from the caller is not propagated; use inputs.
  • The GITHUB_TOKEN permissions can only be reduced by the called workflow, never elevated.
  • Reference with @tag or @sha like actions; a same-repository workflow can use ./.github/workflows/x.yml.

Reusable workflows vs composite actions: a reusable workflow contains whole jobs with their own runners, environments and secrets; a composite action bundles steps inside one job. Use the workflow for "the standard CI pipeline", the action for "set up our toolchain".

Example

# .github/workflows/build.yml in my-org/ci-templates
on:
  workflow_call:
    inputs:
      node-version: {type: string, default: "22"}
    secrets:
      NPM_TOKEN: {required: false}
    outputs:
      artifact: {value: "${{ jobs.build.outputs.name }}"}
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      name: ${{ steps.n.outputs.name }}
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with: {node-version: "${{ inputs.node-version }}"}
      - run: npm ci && npm test
        env: {NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}"}
      - id: n
        run: echo "name=build-${{ github.sha }}" >> "$GITHUB_OUTPUT" 

Remember

  • on: workflow_call makes a workflow callable at job level
  • Inputs, secrets and outputs form its typed interface
  • Reusable workflow = whole jobs; composite action = steps

Watch out for

  • Expecting the caller's env values to reach the reusable workflow; pass them as inputs.

Check yourself

  1. Where do you put uses: org/repo/.github/workflows/ci.yml@v1?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.github.com/en/actions/sharing-automations/reusing-workflows