Advanced GitHub Actions Workflows


Introduction





GitHub Actions has evolved beyond simple CI/CD into a full-featured automation platform. Teams managing monorepos, multi-service architectures, or compliance-sensitive deployments need advanced workflows that are maintainable, fast, and secure. This article explores production-ready patterns for GitHub Actions at scale.





Reusable Workflows





Reusable workflows eliminate duplication across repositories. Define a workflow in `.github/workflows/deploy-shared.yml` with `workflow_call`:






# .github/workflows/deploy-shared.yml


name: Shared Deployment Workflow


on:


workflow_call:


inputs:


environment:


required: true


type: string


image-tag:


required: true


type: string


secrets:


CLOUD_PROVIDER_KEY:


required: true




jobs:


deploy:


runs-on: ubuntu-latest


environment: ${{ inputs.environment }}


steps:


- uses: actions/checkout@v4


- name: Deploy to ${{ inputs.environment }}


run: |


echo "Deploying ${{ inputs.image-tag }} to ${{ inputs.environment }}"


# Actual deployment logic here







Consume it from any repository:






# .github/workflows/release.yml


name: Release


on:


push:


branches: [main]


jobs:


call-deploy:


uses: org/shared-workflows/.github/workflows/deploy-shared.yml@v1


with:


environment: staging


image-tag: ${{ github.sha }}


secrets:


CLOUD_PROVIDER_KEY: ${{ secrets.CLOUD_PROVIDER_KEY }}







Matrix Builds





Matrix strategies test across multiple dimensions without duplicating workflow YAML:






jobs:


test:


runs-on: ubuntu-latest


strategy:


matrix:


node: [18, 20, 22]


os: [ubuntu-latest, windows-latest]


include:


- node: 22


os: ubuntu-latest


coverage: true


exclude:


- node: 18


os: windows-latest


steps:


- uses: actions/checkout@v4


- uses: actions/setup-node@v4


with:


node-version: ${{ matrix.node }}


- run: npm ci


- run: npm test


- if: matrix.coverage


uses: codecov/codecov-action@v3







The `include` key adds jobs to the matrix, while `exclude` removes specific combinations. For large matrices, use `max-parallel: 3` to avoid saturating runner capacity.





Composite Actions





Composite actions bundle multiple steps into a reusable unit, ideal for organization-wide standards:






# .github/actions/setup-node-env/action.yml


name: "Setup Node.js Environment"


description: "Configures Node with pnpm, cache, and dependency audit"


inputs:


node-version:


description: "Node.js version"


required: false


default: "20"


working-directory:


description: "Directory containing package.json"


required: false


default: "."


runs:


using: "composite"


steps:


- uses: actions/setup-node@v4


with:


node-version: ${{ inputs.node-version }}


- uses: pnpm/action-setup@v2


with:


version: 8


- name: Get pnpm store directory


id: pnpm-cache


shell: bash


run: |


echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT


- uses: actions/cache@v3


with:


path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}


key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}


restore-keys: |


${{ runner.os }}-pnpm-


- name: Install dependencies


shell: bash


working-directory: ${{ inputs.working-directory }}


run: pnpm install --frozen-lockfile


- name: Security audit


shell: bash


run: pnpm audit --audit-level=high







Usage in any workflow:






jobs:


build:


runs-on: ubuntu-latest


steps:


- uses: actions/checkout@v4


- uses: ./.github/actions/setup-node-env


with:


node-version: "22"


- run: pnpm build







OIDC and Cloud Authentication





OpenID Connect eliminates static credentials by exchanging GitHub's JWT tokens for cloud provider credentials:






jobs:


deploy:


runs-on: ubuntu-latest


permissions:


id-token: write # Required for OIDC


contents: read


steps:


- uses: actions/checkout@v4


- uses: aws-actions/configure-aws-credentials@v4


with:


role-to-assume: arn:aws:iam::123456789012:role/github-actions-role


aws-region: us-east-1


- run: aws s3 sync ./dist s3://my-bucket







Configure the AWS IAM role with a trust policy scoped to specific repositories and branches:






{


"Version": "2012-10-17",


"Statement": [{


"Effect": "Allow",


"Principal": { "Federated": "arn:aws:iam::ACCOUNT:oidc-provider/token.actions.githubusercontent.com" },


"Action": "sts:AssumeRoleWithWebIdentity",


"Condition": {


"StringLike": {


"token.actions.githubusercontent.com:sub": "repo:org/my-repo:ref:refs/heads/main"


}


}


}]


}







Environment Protection Rules





Protect production deployments with required reviewers, wait gates, and custom deployment branch policies:






name: Deploy


on:


workflow_dispatch:


inputs:


environment:


description: "Target environment"


required: true


default: "production"


type: choice


options:


- staging


- production




jobs:


deploy:


runs-on: ubuntu-latest


environment:


name: ${{ github.event.inputs.environment || 'staging' }}


url: https://${{ github.event.inputs.environment }}.myapp.com


concurrency:


group: ${{ github.event.inputs.environment }}


cancel-in-progress: false


steps:


- run: echo "Deploying to ${{ github.event.inputs.environment }}"







Configure required reviewers in the repository's environment settings to enforce manual approval gates.





Self-Hosted Runners





Self-hosted runners provide custom hardware, internal network access, and reduced costs:






jobs:


build:


runs-on: [self-hosted, linux, x64, gpu]


steps:


- run: nvidia-smi # Verify GPU availability


- uses: actions/checkout@v4


- run: docker build -t my-model:latest .







Scale self-hosted runners with the `actions-runner-controller` on Kubernetes, which auto-provisions and auto-scales runner pods based on workflow demand.





Caching Strategies





Effective caching reduces workflow runtime significantly:






- uses: actions/cache@v3


with:


path: |


~/.cache/go-build


~/go/pkg/mod


key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}


restore-keys: |


${{ runner.os }}-go-







For monorepos, cache per workspace directory:






- uses: actions/cache@v3


with:


path: packages/*/node_modules/.cache


key: ${{ runner.os }}-${{ github.workflow }}-${{ hashFiles('packages/*/package-lock.json') }}







These patterns form the foundation of scalable, secure GitHub Actions usage in enterprise environments. Start with reusable workflows and matrix builds, then layer on OIDC and self-hosted runners as your infrastructure grows.