Create reusable workflows in GitHub Actions

Reusable workflows in GitHub are a game-changer for organizations aiming to fortify their automation infrastructure. Embracing the principle of DRY (Don't Repeat Yourself), reusable workflows usher in standardization, allowing organizations and even entire enterprises to avoid redundancy and capitalize on consistent implementation of best practices. They help create paved paths for developers, making processes more streamlined and efficient, without imposing undue restrictions. This guide covers the implementation and deployment of reusable workflows to refine and elevate your GitHub Actions infrastructure. As we move along, we will be joined by Deutsche Vermögensberatung AG (DVAG) for their advice on getting the most out of reusable workflows.
In this guide, you will learn how to:
Understand the essential components and syntax required to create a reusable workflow in Actions
Discover the URL structure and syntax for invoking a reusable workflow in Actions
Learn how to incorporate your reusable workflow into projects housed in separate repositories
Make your first reusable workflow
1. Make it reusable.
For a workflow to be reusable, the values for on
must include workflow_call
:
on:
workflow_call:
2. Define inputs.
You can define inputs and secrets in a reusable workflow to receive them from a caller workflow. Use the inputs
and secrets
keywords for this purpose. Below is a commented version of an example reusable workflow:
on: # Specifies the event triggering the workflow
workflow_call: # Indicates that this is a reusable workflow
inputs: # Defines the inputs that can be passed from the caller workflow
config-path: # Name of the input
required: true # Specifies that this input is mandatory
type: string # Specifies the type of the input
secrets: # Defines the secrets that can be passed from the caller workflow
envPAT: # Name of the secret
required: true # Specifies that this secret is mandatory
In the example above, envPAT
is an environment secret that's been added to the production environment. This environment is therefore referenced within the job.
3. Pass named secrets
To pass named inputs to a called workflow, use the with
keyword in a job. Use the secrets
keyword to pass named secrets. For inputs, the data type of the input value must match the type specified in the called workflow (either boolean, number, or string).
jobs:
call-workflow-passing-data:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
with:
config-path: .github/labeler.yml
secrets:
envPAT: ${{ secrets.envPAT }}
Workflows that call reusable workflows in the same organization or enterprise can use the inherit
keyword to implicitly pass the secrets.
jobs:
call-workflow-passing-data:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
with:
config-path: .github/labeler.yml
secrets: inherit
We rely on Dependabot to automatically distribute updates of our reusable workflows across teams and projects. Each release automatically triggers a pull request in the user repositories, which we closely track in a dedicated issue. This lets us keep tabs on who has and hasn’t updated their workflows and gives us a streamlined way to ensure everyone is on the same page, using the most current and effective workflows.

Calling reusable Actions
You call a reusable workflow by using the uses
keyword. Unlike when you are using Actions within a workflow, you call reusable workflows directly within a job, and not from within job steps. Let's break down the URL structure for calling a reusable workflow in Actions:
uses: [OWNER/REPOSITORY PATH]/[.github/workflows/WORKFLOW_FILE]@[REF]
Each segment of this structure has a specific purpose:
1. uses
:
This is a keyword in the Actions syntax that indicates the following string will reference a reusable piece of code, such as an Action or, in this case, a reusable workflow.
2. OWNER
:
This specifies the GitHub user or organization that owns the repository where the reusable workflow is located (ex: octo-org).
3. REPOSITORY PATH
:
This is the path to the repository (ex: example-repo/
).
4. .github/workflows/
:
This is a conventional path where Actions expects to find workflow definitions in a repository. All workflow YAML files should be located within this directory for them to be recognized and executed by Actions.
5. WORKFLOW_FILE
:
This specifies the exact YAML file that contains the workflow definition. (ex: reusable-workflow.yml
).
6. @REF
:
This indicates the branch, tag, or commit SHA from which the workflow should be used. It's essential to specify this so that Actions knows which version of the workflow to use. This is particularly useful if workflows evolve over time, and you want to ensure a specific version is used in your own workflow (ex: @main
).
Here’s an example from the build-test-deploy.yml
workflow we created in our earlier guides. You can see how we created the original version in the Automation Essentials module. Below we updated the file to accommodate reusability. In addition to adding workflow_call
, we also defined parameters allowing us to set the node-version
. Pay attention to the comments outlined in the code:
name: build-test-deploy
# Changed 'on' to enable this workflow to be called from other workflows
on:
workflow_call:
# Introduced 'inputs' to define parameters that can be passed when calling this workflow
inputs:
node-version:
description: "Node version"
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout repo
uses: actions/checkout@v3
# Modified to use the node-version from the workflow inputs
- name: use node.js
uses: actions/setup-node@v3
with:
node-version: ${{ inputs.node-version }}
- run: npm install
- run: npm run build
test:
needs: build
runs-on: ubuntu-latest
steps:
- name: checkout repo
uses: actions/checkout@v3
# Modified to use the node-version from the workflow inputs
- name: use node.js
uses: actions/setup-node@v3
with:
node-version: ${{ inputs.node-version }}
- run: npm install
- run: npm run test
We centralize our workflows in a monorepo where all changes require code-owner approval to ensure consistency and quality over time, while also enabling specialized ownership for certain workflows. We use semantic versioning and avoid breaking changes by sticking to a 1.x.x version scheme. We generally release new workflow versions weekly, using auto-merge functionality to keep updates flowing and testing them on both the main branch and on test repos. We also actively monitor workflow use across repositories, so we can ensure they’re being used effectively.

Now that we have updated our build-test-deploy
workflow to accommodate reusability and we understand how to call a reusable workflow, let's put it all together. Below we will utilize build-test-deploy
in a workflow located in a separate repository:
jobs:
my-job:
# Importing a reusable workflow from another repository and branch
uses: organization/repo/.github/workflows/build-test-deploy.yml@main
# Passing 'node-version' as an input parameter to the reusable workflow
with:
node-version: '18.x'
Up next: Manage and monitor workflows in GitHub Actions
Ready to optimize how you manage and monitor your Actions? Check out our in-depth guide. From analyzing workflow statuses to tracking resource usage, we'll arm you with the tools you need to operate more efficiently.