Skip to main content

How to create a reusable GitHub Actions workflow

Create a callable GitHub Actions workflow with inputs, secrets, and outputs so multiple repositories share one tested CI definition instead of copying YAML.

Difficulty
Intermediate
Duration
45 minutes
Steps
6

What and why

Copying the same CI YAML into many repositories creates a maintenance trap: when the build logic changes, you must edit every copy, and the copies drift apart over time. A reusable workflow solves this by defining the logic once in one repository and letting other workflows call it with parameters, much like calling a function. This keeps CI consistent across a fleet of repositories and means a single change propagates everywhere that calls it.

Reusable workflows differ from composite actions: a reusable workflow can contain multiple jobs, run on different runners, and use environments, while a composite action bundles steps within a single job. For sharing a whole pipeline, the reusable workflow is the right tool.

Prerequisites

  • An existing GitHub Actions workflow you want to share.
  • Two or more repositories, or workflows, that need the same logic.
  • Familiarity with how triggers, jobs, and contexts work.

Steps

1. Add the workflow_call trigger

In the shared repository, create .github/workflows/reusable-ci.yml and make it callable by exposing the workflow_call event:

name: Reusable CI
on:
  workflow_call:

A workflow becomes reusable purely by declaring this trigger. It can still also declare other triggers if it runs standalone too.

2. Declare inputs and secrets

on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: '20'
    secrets:
      npm-token:
        required: false

Inputs are typed parameters with optional defaults, forming the workflow's public interface. Secrets are not inherited automatically; callers must pass them explicitly, which keeps secret flow auditable. Mark a secret required: true when the workflow cannot function without it.

3. Use inputs in jobs

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: npm ci && npm test

Reference parameters with the inputs context anywhere in the workflow. The job behaves exactly as a normal job; the only difference is where its parameters come from.

4. Define workflow outputs

Expose results back to the caller so it can act on them:

    outputs:
      result: ${{ jobs.ci.outputs.result }}

This maps a job-level output up to the workflow level. The caller reads it from the needs context of the calling job, enabling patterns like deploy-after-build across the boundary.

5. Call the workflow from a caller

In another repository's workflow, call the shared workflow at the job level:

jobs:
  call-ci:
    uses: my-org/shared/.github/workflows/reusable-ci.yml@main
    with:
      node-version: '22'
    secrets:
      npm-token: ${{ secrets.NPM_TOKEN }}

The uses key points to the shared workflow by path and ref. The with block sets inputs and secrets passes secrets through. Note that the caller uses uses at the job level, not the step level.

Verification

Push both repositories and trigger the caller. In the Actions tab, the run displays the reusable workflow nested under the calling job, with its own steps visible. Change node-version in the caller from 22 to 20 and confirm the runtime version in the logs changes without editing the shared file. This proves the parameterization works.

Next Steps

Pin the reusable workflow to a release tag instead of a branch so callers get stable, intentional updates. Add required inputs to enforce correct usage and fail fast on misconfiguration. Combine a reusable workflow with a matrix in the caller to fan out across versions. Document the inputs and outputs so other teams can adopt the shared workflow confidently.

Prerequisites

  • A working GitHub Actions workflow
  • Two or more repos sharing CI logic
  • Understanding of workflow triggers

Steps

  • 1
    Add the workflow_call trigger
  • 2
    Declare inputs and secrets
  • 3
    Use inputs in jobs
  • 4
    Define workflow outputs
  • 5
    Call the workflow from a caller
  • 6
    Verify reuse

Category

CI/CD