Stop Copying CI YAML: Use GitHub Actions Reusable Workflows
Standardize pipelines across repositories with workflow_call, pinned SHAs, and explicit secret mapping.
05 Mar 2026, 07:02 UTC

The problem: CI YAML drift across repositories
You push a dependency update to a microservice, and its CI pipeline fails because a teammate changed a test command in a shared template last week. The change landed in three repositories, but the other nine still run the old command. This drift is common when teams copy `.github/workflows/ci.yml` from one repo to another instead of maintaining a single definition.
The useful takeaway: define the workflow once in a central repository, call it from other repositories with workflow_call, pin the reference to a commit SHA, and pass only the secrets each job actually needs.
What a reusable workflow looks like
A reusable workflow is a normal GitHub Actions workflow that uses the workflow_call trigger. It lives in .github/workflows/ of a source repository, often one dedicated to shared tooling.
name: Reusable CI
on:
workflow_call:
inputs:
node-version:
description: 'Node.js version to install'
type: string
default: '20'
outputs:
status:
description: 'Result of the build job'
value: ${{ jobs.build.outputs.status }}
jobs:
build:
runs-on: ubuntu-latest
outputs:
status: ${{ steps.done.outputs.status }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: npm test
- id: done
run: echo 'status=passed' >> $GITHUB_OUTPUT
The workflow_call event means this workflow cannot be run manually or on a push; it exists to be called by another repository. Inputs are typed, and outputs let the caller make decisions based on the shared job's result.
Call it from another repository
In a consumer repository, add a workflow file such as .github/workflows/ci.yml. Run these commands in a local clone of that repository:
# Create a feature branch; do not push directly to main without review
git checkout -b use-reusable-ci
# Create or edit .github/workflows/ci.yml, then stage and commit
git add .github/workflows/ci.yml
git commit -m 'Use reusable CI workflow from shared repository'
git push origin use-reusable-ci
Required permission: write access to the consumer repository, or the ability to open a pull request against it. Organization policies may also restrict which repositories can reuse workflows, so check Settings > Actions > General in your organization if the call fails.
The caller workflow references the shared workflow by repository, path, and a full commit SHA:
name: Application CI
on:
push:
branches: [main]
jobs:
shared-ci:
uses: octo-org/shared-workflows/.github/workflows/reusable-ci.yml@a1b2c3d4e5f678901234567890abcdef12345678
with:
node-version: '20'
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Replace octo-org/shared-workflows and the SHA with your own source repository and commit. Pinning to a SHA means the caller keeps using that exact version even if the source repository's default branch moves forward. Using a branch name such as @main is convenient but risks silent pipeline changes when the shared workflow is updated.
Secrets, permissions, and what breaks
The reusable workflow runs in the caller's context, using the caller's permissions and tokens. You pass secrets explicitly, as shown above, or with secrets: inherit. Avoid secrets: inherit unless you truly need every repository secret inside the shared workflow; it over-exposes credentials.
Keep these limitations in mind:
- Logs and artifacts belong to the caller run. A failure inside the shared definition still appears in the caller's Actions tab, but artifact names and retention follow the caller's settings, which can complicate debugging.
- Matrix strategies and environment protection rules must be re-declared in the caller; they do not inherit from the reusable workflow.
- Reusable workflows cannot cross certain GitHub Enterprise boundaries without explicit access configuration.
Verify the result
After the push, open the consumer repository on GitHub and click Actions. Confirm the run appears under the consumer repository, not the source repository. Open the run and check the job's uses line to verify it references the pinned SHA.
To confirm pinning works, merge a change into the source repository's default branch. The consumer should continue running the pinned SHA until you update the reference manually. If you need to roll back because the new caller workflow causes failures, revert the commit or close the pull request that added it.
Closing
Pick one pipeline that is copied across multiple repositories, move it into a shared workflow, and call it from a single consumer repository first. Pin the SHA, map secrets explicitly, and verify the run appears in the caller's Actions tab. Once that works, expand to the rest of your repositories.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.