GitHub Actions Reusable Workflows: Stop Repeating Yourself
Create reusable GitHub Actions workflows with inputs, secrets, and outputs to eliminate YAML duplication across repositories and teams efficiently.
Tags
GitHub Actions Reusable Workflows: Stop Repeating Yourself
TL;DR
Reusable workflows let you define CI/CD pipelines once and call them from multiple repositories using workflow_call. Stop copying YAML files between repos and start treating your pipelines like shared functions.
The Problem
Every new repository gets the same CI/CD boilerplate — lint, test, build, deploy. You copy the workflow YAML, tweak a few values, and move on. Six months later, you need to update the Node.js version across all pipelines. Now you are editing the same YAML in twelve repositories and hoping you do not miss one.
Composite actions help with individual steps, but they cannot define entire jobs or control job ordering. You need something that encapsulates a complete workflow — jobs, steps, conditions, and all — and exposes it as a callable unit.
The Solution
Create a reusable workflow with the workflow_call trigger. This turns a workflow file into a function that other workflows can invoke with inputs and secrets.
Step 1: Define the reusable workflow.
# .github/workflows/ci-pipeline.yml (in your shared repo)
name: CI Pipeline
on:
workflow_call:
inputs:
node-version:
description: 'Node.js version to use'
required: false
type: string
default: '20'
working-directory:
description: 'Directory containing package.json'
required: false
type: string
default: '.'
secrets:
NPM_TOKEN:
required: false
jobs:
lint-and-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- run: npm run lint
- run: npm test
build:
needs: lint-and-test
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run buildStep 2: Call it from any repository.
# .github/workflows/ci.yml (in your project repo)
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ci:
uses: your-org/shared-workflows/.github/workflows/ci-pipeline.yml@main
with:
node-version: '20'
secrets: inherit # Pass all secrets from the callerPassing secrets explicitly when you need fine-grained control:
jobs:
ci:
uses: your-org/shared-workflows/.github/workflows/ci-pipeline.yml@main
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Composite actions vs. reusable workflows: Composite actions bundle multiple steps into a single reusable step. Reusable workflows encapsulate entire jobs with their own runners and job dependencies. Use composite actions for shared step sequences (like "setup Node + install deps"). Use reusable workflows for shared pipelines (like "lint + test + build + deploy").
Why This Works
Reusable workflows centralize pipeline logic. When you update the shared workflow — add a security scan step, bump a dependency version, change the test command — every repository that calls it picks up the change automatically. Pin to a specific tag (@v1) for stability, or reference @main for always-latest behavior.
The secrets: inherit shorthand passes all caller secrets without listing them individually, which keeps the calling workflow minimal. For repositories that need different behavior, inputs provide the escape hatch without forking the entire workflow.
FAQ
What is a reusable workflow in GitHub Actions?
It is a workflow that uses the workflow_call trigger, allowing other workflows to call it like a function with inputs, secrets, and outputs.
How do I pass secrets to a reusable workflow?
Define secrets in the reusable workflow's on.workflow_call.secrets section, then pass them from the caller workflow using the secrets key or secrets: inherit.
Can reusable workflows be in a different repository?
Yes, reference them with the full path like owner/repo/.github/workflows/reusable.yml@main, and the repository must be public or in the same organization.
Collaboration
Need help with a project?
Let's Build It
I help startups and established companies design, build, and scale world-class digital products. From deep technical architecture to pixel-perfect UI — let's bring your vision to life.
Related Articles
TypeScript Utility Types You Should Know
Five essential built-in generic utility types in TypeScript that will save you hundreds of lines of code.
Generate Dynamic OG Images in Next.js
Generate dynamic Open Graph images in Next.js using the ImageResponse API with custom fonts, gradients, and data-driven content for social sharing.
Node.js Streams for Processing Large Files
Process large files efficiently in Node.js using readable, writable, and transform streams to avoid memory issues and handle data chunk by chunk.