🧩 GitHub Actions Composite vs Reusable Workflows

How to standardize and supercharge your CI/CD pipelines across projects When your teams…

How to standardize and supercharge your CI/CD pipelines across projects

When your teams manage multiple projects with similar deployment patterns, repeating the same GitHub Actions steps over and over can become tedious, error-prone, and hard to maintain

Thankfully, GitHub Actions offers two powerful solutions to help standardize, reuse, and scale your CI/CD pipelines: Composite Actions and Reusable Workflows. When used together, they form a clean, modular, and DRY (don’t repeat yourself) CI/CD strategy

πŸ”„ Composite Actions vs Reusable Workflows

🧱 Composite Actions

Composite Actions allow you to group steps (like docker build, terraform plan, or trivy scan) into a reusable component. Think of it like a function.

Best for:

  • Reusing logic (e.g., build and push images)
  • Hiding complex logic from the main workflow
  • Keeping workflows minimal and maintainable

Use it with:

uses: ./.github/actions/your-action

πŸ” Reusable Workflows

Reusable Workflows are full workflows that can be invoked with inputs and secrets.

Best for:

  • High-level CI/CD orchestration (build, test, scan, deploy)
  • Enforcing common practices across repositories
  • Abstracting full pipelines

Use it with:

uses: org/repo/.github/workflows/your-workflow.yml@ref

🐳 Example: Docker Build and Scan with Trivy

Here is an example of a composite action to build and push a Docker image to Azure Container Registry (ACR) and scan it using Trivy.

.github/actions/build-and-scan/action.yml

name: Docker Build and Push

inputs:
  dockerfile:
    default: Dockerfile
  context:
    default: .
  image_name:
    required: true
  tag:
    required: true
  build_args:
    default: ""
  report_name:
    default: trivy-report

runs:
  using: "composite"
  steps:
    - uses: azure/login@v2
      with:
        creds: ${{ env.AZURE_CREDENTIALS }}

    - uses: azure/docker-login@v2
      with:
        login-server: ${{ env.CONTAINER_REGISTRY }}
        username: ${{ env.ACR_USERNAME }}
        password: ${{ env.ACR_PASSWORD }}

    - name: Build and Push Image
      shell: bash
      run: |
        docker build ${{ inputs.build_args }} \
          -t ${{ env.CONTAINER_REGISTRY }}/${{ inputs.image_name }}:${{ inputs.tag }} \
          -f "${{ inputs.dockerfile }}" "${{ inputs.context }}"
        docker push ${{ env.CONTAINER_REGISTRY }}/${{ inputs.image_name }}:${{ inputs.tag }}

    - name: Scan with Trivy
      uses: aquasecurity/trivy-action@0.32.0
      with:
        image-ref: '${{ env.CONTAINER_REGISTRY }}/${{ inputs.image_name }}:${{ inputs.tag }}'
        format: 'sarif'
        output: 'trivy-results.sarif'
        exit-code: 0

    - name: Upload SARIF to GitHub Security tab
      uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: 'trivy-results.sarif'
        category: trivy

Usage in Workflow

- name: Build and Scan
  uses: ./.github/actions/build-and-scan
  with:
    image_name: my-app
    tag: ${{ github.sha }}

🌍 Example: Terraform/Terragrunt Validation with Checkov

Now let’s see a reusable workflow to validate Terraform code and scan for misconfigurations using Checkov.

.github/workflows/infra-check.yml

name: Infra Validation Workflow

on:
  workflow_call:
    inputs:
      working_dir:
        required: true
        type: string

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Terragrunt and OpenTofu
        run: |
          curl -s https://raw.githubusercontent.com/opentofu/opentofu/main/scripts/install.sh | bash
          curl -L https://github.com/gruntwork-io/terragrunt/releases/latest/download/terragrunt_linux_amd64 -o terragrunt
          chmod +x terragrunt
          sudo mv terragrunt /usr/local/bin/

      - name: Validate HCL Syntax
        run: |
          terragrunt hclfmt --terragrunt-check --terragrunt-diff --recursive
        working-directory: ${{ inputs.working_dir }}

      - name: Check Misconfigurations with Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: ${{ inputs.working_dir }}
          quiet: true

Usage in Project Workflow

jobs:
  call-validation:
    uses: your-org/your-repo/.github/workflows/infra-check.yml@main
    with:
      working_dir: infrastructure/staging/eu-west-1

βš™οΈ Best Practices

βœ… Keep composite actions in .github/actions βœ… Keep reusable workflows in .github/workflows βœ… Abstract frequently repeated patterns βœ… Use inputs, outputs, and env vars to increase flexibility βœ… Use workflow_call and workflow_dispatch to structure triggers βœ… Defer secrets to the calling workflow

πŸ’‘ Conclusion

Using composite actions and reusable workflows together can transform your CI/CD processes into clean, repeatable, and secure pipelines. Whether you’re building containers, provisioning infrastructure, or scanning for vulnerabilities, this modular approach will scale with your teams and your systems.

Enjoy building better pipelines !