GitHub Actions in Depth
Topic 58

Reusable Workflows and Composite Actions

CI/CD

Both reusable workflows and composite actions exist to stop you copy-pasting YAML, but they operate at different levels and are not interchangeable. A reusable workflow is a whole pipeline — its own jobs, its own runners — that another workflow invokes. A composite action bundles a sequence of steps behind a single uses: and runs inside a caller's job. Pick the wrong one and you spend the day fighting the tool.

The deciding question is altitude. If the thing you want to share needs multiple jobs, a matrix, or an environment gate, it has to be a reusable workflow. If it is a repeated run of steps inside one job — checkout, auth, build — it is a composite action.

Two levels of reuse
Reusable workflow
Brings its own jobs and runners. Triggered with workflow_call and invoked at the job level by another workflow. Use it when the shared unit needs multiple jobs, a matrix, or an environment gate.
Composite action
Packages a sequence of steps only — no jobs of its own. Called with uses: and runs inside the caller's job on its runner. Use it for a repeated run of steps like checkout, auth, build.

Reusable Workflows

A reusable workflow declares on: workflow_call and lists its inputs, secrets, and outputs. A caller invokes it with uses: at the job level, and the called workflow runs its own jobs on its own runners.

A reusable workflow and its caller
# .github/workflows/build.yml
on:
  workflow_call:
    inputs:
      node: { type: string, default: '20' }
    secrets:
      TOKEN: { required: true }
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

# caller
jobs:
  call-build:
    uses: ./.github/workflows/build.yml
    with: { node: '22' }
    secrets: inherit

Composite Actions

A composite action is an action.yml with runs.using: "composite" and a list of steps. It is referenced as one step inside any job and executes in that job's context — same filesystem, same token, same working directory.

A composite action.yml
name: setup-app
runs:
  using: "composite"
  steps:
    - uses: actions/setup-node@v4
      with: { node-version: 20 }
    - run: npm ci
      shell: bash

Inputs, Outputs, Secrets

Composite actions take inputs and expose outputs like any action. Reusable workflows are stricter about secrets: nothing is inherited automatically, so you either list each secret under secrets: or pass secrets: inherit deliberately. Declaring typed inputs with defaults is what keeps a shared unit usable across repos.

Nesting and Limits

Reusable workflows nest up to ten levels deep, and a single workflow file can call up to fifty unique reusable workflows. Push past the nesting limit and the run fails outright, so deeply layered pipelines need to be flattened rather than chained further.

Choosing Between Them

Reach for a reusable workflow when you are sharing orchestration — multiple jobs, a matrix, environment gating. Reach for a composite action when you are packaging a step sequence that runs inside one existing job. Getting this split right up front avoids rewriting the shared unit later.

Reusable Workflow vs Composite Action

Reusable workflow — defines its own jobs and runs-on, can use a matrix and environments, and is called at the job level with explicit secrets: passing. Use it to share an entire pipeline.

Composite action — a sequence of steps with no job or runner of its own, called as one step inside the caller's job and inheriting that job's context and token. Use it to package a repeated step sequence like setup plus auth plus build. If you need multiple jobs or environment gating, it must be a reusable workflow.

Common Mistakes
  • Reaching for a composite action when you need multiple jobs or an environment gate — composite actions are steps, not jobs.
  • Forgetting to pass secrets: to a reusable workflow — secrets are not inherited unless you write secrets: inherit or list them.
  • Assuming a composite action gets its own runner, when it runs in the caller's job and sees the caller's filesystem and token.
  • Hardcoding values that should be inputs, so the "reusable" unit only works for the one repo it was written in.
  • Nesting reusable workflows beyond ten levels and hitting the hard limit, which fails the run.
Best Practices
  • Use a reusable workflow with workflow_call to share a full pipeline that has jobs, a matrix, or environments.
  • Use a composite action to package a repeated step sequence within a single job.
  • Declare typed inputs with defaults and pass secrets explicitly, using secrets: inherit only when justified.
  • Expose outputs from reusable workflows so callers can branch on the results.
  • Pin reusable workflows and composite actions to a commit SHA like any other uses: reference.
Comparable toolsGitLab CI/CD include, CI/CD components, extendsCircleCI orbs and reusable commands/jobsAzure Pipelines step, job, and stage templatesJenkins shared libraries

Knowledge Check

Which construct can define its own multiple jobs?

  • A reusable workflow, which has its own jobs and runs-on
  • A composite action, which can declare jobs under runs
  • Both, since they are just two syntaxes for the same thing
  • Neither; only the top-level workflow can have jobs

How do secrets reach a reusable workflow?

  • They must be passed explicitly, by listing them under secrets: or using secrets: inherit
  • They are inherited automatically from the calling workflow with no extra config
  • They are read directly from the called workflow's own repository-level secret settings page
  • Reusable workflows cannot receive any secrets from the caller at all

Why does a composite action share the caller's context?

  • It has no job or runner of its own and executes as a step inside the caller's job
  • It clones the caller's environment onto a fresh runner before its steps start
  • It explicitly imports the caller's filesystem through a declared action input
  • It does not; each composite action runs in a fully isolated context

What is the nesting limit for reusable workflows?

  • Up to ten levels deep, with up to fifty unique reusable workflows from one caller
  • Unlimited depth, constrained only by the job's overall run-time budget
  • Two levels deep, the very same hard cap that applies to nested composite-action calls
  • Sixteen levels deep with no limit on workflows per caller

You got correct