You can reuse workflow templates across projects, and the only reason you’d ever do it is to maintain consistency in your CI/CD processes and reduce duplicated effort.

Let’s see how this looks in practice with GitHub Actions.

Imagine you have a standard set of tests and deployments you want to run for all your Python projects. Instead of copying and pasting the same build.yml into each repository, you can create a central reusable workflow.

Here’s a ci.yml in a central repository (let’s call it org-workflows):

# org-workflows/.github/workflows/python-ci.yml
name: Python CI

on:
  workflow_call: # This makes it reusable
    inputs:
      python_version:
        description: 'Python version to use'
        required: true
        type: string
        default: '3.9'
      test_command:
        description: 'Command to run tests'
        required: false
        type: string
        default: 'pytest'

jobs:
  build_and_test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Set up Python ${{ inputs.python_version }}

        uses: actions/setup-python@v4
        with:

          python-version: ${{ inputs.python_version }}

      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests

        run: ${{ inputs.test_command }}

Now, in any of your project repositories (e.g., my-python-app), you can call this workflow. Here’s my-python-app/.github/workflows/main.yml:

# my-python-app/.github/workflows/main.yml
name: Main CI Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  run_python_ci:
    uses: your-github-username/org-workflows/.github/workflows/python-ci.yml@main # This is the magic!
    with:
      python_version: '3.10'
      test_command: 'pytest --cov=my_app'

When a push or pull_request event happens on the main branch of my-python-app, the run_python_ci job will execute the python-ci.yml workflow from the org-workflows repository. It passes 3.10 as the Python version and specifies a custom test command.

The core problem this solves is the "copy-paste-and-pray" anti-pattern. Without reusable workflows, every time you need to update your testing framework, add a new linting step, or change your deployment strategy, you’d have to manually edit dozens, or even hundreds, of workflow files across different repositories. This is error-prone and incredibly time-consuming. Reusable workflows centralize this logic.

Internally, GitHub Actions treats a workflow_call trigger differently. When a workflow is triggered via workflow_call, it doesn’t run its own on: events. Instead, it waits to be invoked by another workflow. The uses: keyword in the calling workflow tells GitHub Actions to fetch the specified workflow file from the given repository and ref (branch or tag). It then executes the jobs defined in that remote workflow, passing any with: inputs as parameters.

The inputs section in the reusable workflow defines the contract for how other workflows can interact with it. These inputs are strongly typed (string, boolean, number, array, object) and can be marked as required. The calling workflow must provide values for required inputs, and can optionally override defaults for any input.

The secrets can also be passed down. If your reusable workflow needs access to a GitHub token or an external API key, you can define an inputs for secrets.GITHUB_TOKEN or a custom secret, and then pass it from the calling workflow.

The ref in the uses: statement is crucial. Using @main means you’re always pulling the latest code from the main branch. For production stability, you should almost always use a specific tag (e.g., @v1.2.0) or a commit SHA to ensure that your pipeline doesn’t break unexpectedly when someone merges changes to the main branch of your workflow repository. This provides a stable, auditable reference point for your CI/CD.

The most surprising thing most people miss is that reusable workflows can also trigger other reusable workflows. You can build complex, layered CI/CD pipelines where a top-level workflow calls a reusable workflow that, in turn, calls another reusable workflow for a specific task like code signing or vulnerability scanning. This allows for extreme modularity and abstraction, making your overall CI/CD infrastructure much easier to manage and reason about.

The next logical step is to explore how to pass secrets securely into these reusable workflows.

Want structured learning?

Take the full Argo-workflows course →