If you manage CI/CD for more than a handful of repositories, you have probably copy-pasted the same GitHub Actions YAML more than once. Reusable workflows solve this: define the logic once in a central repo, call it from everywhere else, update it once and all callers get the fix.
The problem with copy-paste pipelines
When a security team asks you to pin all action versions to a commit SHA, or when a new container registry replaces the old one, you are looking at a PR per repo. With 40 repos that is 40 PRs, 40 reviews, and 40 merge operations. Reusable workflows reduce that to one change in one place.
Creating a reusable workflow
# .github/workflows/build-and-push.yml in your "platform" repo
on:
workflow_call:
inputs:
image_name:
required: true
type: string
secrets:
REGISTRY_TOKEN:
required: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/my-org/${{ inputs.image_name }}:${{ github.sha }}
Calling a reusable workflow
jobs:
build:
uses: my-org/platform/.github/workflows/build-and-push.yml@v2.1.0
with:
image_name: my-service
secrets:
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Composite actions vs reusable workflows
- Composite actions — a sequence of steps embedded inside any job. No separate runner.
- Reusable workflows — a full workflow with its own jobs and runners. Better for complex pipelines.
Real-world impact
After centralising 8 common pipeline patterns across 35 repositories, our team reduced per-repo CI configuration by an average of 60%. When we needed to add SBOM generation to every build, it was a single PR instead of 35.