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: inheritThe 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.
envfrom the caller is not propagated; use inputs.- The
GITHUB_TOKENpermissions can only be reduced by the called workflow, never elevated. - Reference with
@tagor@shalike 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.