Reusable Workflows & Custom Actions

Once an organization has more than a few repositories, copy-pasted GitHub Actions YAML becomes a maintenance problem. A security fix or a runtime upgrade has to land in fifty places, and they drift apart. GitHub Actions offers two ways to share logic. Reusable workflows share whole jobs (and pipelines of jobs). Custom actions, whether composite, JavaScript, or Docker, share steps.

Used well, they let a platform team offer "golden path" pipelines: every service gets standardized, secure build, test, and deploy stages by calling one versioned workflow with a few inputs.

TL;DR

Quick Example

A reusable Node CI workflow in a central repository:

A service repository calling it:

Core Concepts

Reusable Workflows

A workflow becomes reusable by adding the workflow_call trigger. Callers invoke it at the job level with uses: instead of runs-on/steps. Characteristics:

Composite Actions

A composite action is an action.yml with runs.using: composite and a list of steps. It runs inside the caller's job, on the caller's runner:

JavaScript and Docker Actions

Choosing the Right Mechanism

Versioning and Distribution

Best Practices

Keep Interfaces Small and Opinionated

A reusable workflow with twenty inputs is a harder-to-read copy of YAML. Encode standards (security scanning, caching, least-privilege permissions) internally, and expose only what genuinely varies between services.

Test Shared Workflows Before Release

Maintain example caller repositories or a test workflow in the template repo that exercises each input path. A broken @v2 breaks every service's CI at once.

Document Inputs, Outputs, and Required Permissions

Callers need to know which secrets and permissions to grant (for example id-token: write for OIDC deploys). Put it in the README and in description fields.

Prefer Explicit Secrets for Cross-Boundary Calls

secrets: inherit is convenient inside one organization, but it passes every secret. For sensitive pipelines, pass only the secrets the workflow needs.

Common Mistakes

Mixing Up Where uses: Goes

Composite actions go under steps; reusable workflows replace a job's runs-on/steps.

Forgetting shell: in Composite Actions

Every run: step in a composite action must declare shell: bash (or pwsh, etc.). Omitting it is a validation error.

Referencing a Branch Instead of a Version

uses: acme/ci-templates/.github/workflows/deploy.yml@main means any push to the template's main instantly changes every consumer's pipeline, untested. Pin to tags and roll out updates deliberately.

FAQ

Reusable workflow or composite action?

Use a reusable workflow to standardize entire jobs or pipelines, especially with multiple jobs, environments, or approvals. Use a composite action to package a sequence of steps that callers slot into their own jobs, like "set up toolchain" or "publish coverage".

Can a reusable workflow access the caller's secrets?

Only those passed explicitly under secrets: or all of them with secrets: inherit. Environment secrets come from environments referenced inside the called workflow, not from the caller's job.

Can reusable workflows be private?

Yes. A workflow in a private or internal repository can be called by other repositories in the same organization or enterprise once access is enabled in that repository's Actions settings. Public repositories can call reusable workflows in other public repositories.

How do I debug a failing reusable workflow?

The caller's run page shows each called job with full logs. Enable debug logging by setting the ACTIONS_STEP_DEBUG secret or variable to true. Test changes on a branch by pointing a test caller at @your-branch before tagging a release.

Related Topics

References