GitHub Actions: Advanced CI/CD Workflows for 2026
GitHub Actions CI/CD has evolved from a simple automation tool into a comprehensive platform that handles everything from basic testing to complex multi-environment deployments. This guide goes beyond the basics to cover matrix builds, reusable workflows, composite actions, OIDC authentication, and real patterns for monorepo CI/CD that scale with your organization.
Matrix Builds: Testing Across Versions and Platforms
Matrix strategies let you test across multiple combinations of operating systems, language versions, and configurations in parallel. Combined with fail-fast and max-parallel controls, you can balance thoroughness with speed.
name: CI Pipeline
on: [push, pull_request]
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [18, 20, 22]
exclude:
- os: windows-latest
node: 18
include:
- os: ubuntu-latest
node: 22
coverage: true
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- run: npm test
- if: matrix.coverage
run: npm run test:coverage
- if: matrix.coverage
uses: codecov/codecov-action@v4Reusable Workflows: DRY CI/CD
Reusable workflows let you define common CI/CD patterns once and call them from multiple repositories. This is essential for organizations with dozens of services that share the same build, test, and deploy patterns.
# .github/workflows/reusable-deploy.yml (in shared repo)
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
cluster:
required: true
type: string
secrets:
AWS_ROLE_ARN:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Deploy to EKS
run: |
aws eks update-kubeconfig --name ${{ inputs.cluster }}
kubectl set image deployment/app app=${{ inputs.image-tag }}
kubectl rollout status deployment/app --timeout=5m
# Calling workflow (in service repo)
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy-staging:
uses: org/shared-workflows/.github/workflows/reusable-deploy.yml@main
with:
environment: staging
image-tag: ghcr.io/org/myapp:${{ github.sha }}
cluster: staging-cluster
secrets:
AWS_ROLE_ARN: ${{ secrets.STAGING_ROLE_ARN }}
deploy-production:
needs: deploy-staging
uses: org/shared-workflows/.github/workflows/reusable-deploy.yml@main
with:
environment: production
image-tag: ghcr.io/org/myapp:${{ github.sha }}
cluster: prod-cluster
secrets:
AWS_ROLE_ARN: ${{ secrets.PROD_ROLE_ARN }}GitHub Actions CI/CD: OIDC Authentication
Stop storing long-lived cloud credentials as secrets. OIDC (OpenID Connect) lets GitHub Actions exchange a short-lived token for temporary cloud credentials, eliminating the risk of leaked secrets.
jobs:
deploy:
permissions:
id-token: write # Required for OIDC
contents: read
steps:
# AWS — no access keys needed
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-deploy
aws-region: us-east-1
# GCP — no service account key needed
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123/locations/global/workloadIdentityPools/github/providers/github
service_account: deploy@project.iam.gserviceaccount.com
# Azure — no client secret needed
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}Composite Actions: Reusable Steps
Composite actions bundle multiple steps into a single reusable action. They’re lighter than reusable workflows — perfect for common step sequences like “setup, build, push container image.”
# .github/actions/docker-build-push/action.yml
name: Build and Push Docker Image
description: Builds and pushes a Docker image to GHCR
inputs:
image-name:
required: true
dockerfile:
default: Dockerfile
context:
default: '.'
runs:
using: composite
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
push: true
tags: ghcr.io/${{ github.repository }}/${{ inputs.image-name }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=maxMonorepo CI/CD Patterns
For monorepos, you need to run CI only for changed packages. Use path filters, dependency detection, and conditional job execution to avoid rebuilding everything on every push.
name: Monorepo CI
on:
push:
branches: [main]
pull_request:
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
api: ${{ steps.filter.outputs.api }}
web: ${{ steps.filter.outputs.web }}
shared: ${{ steps.filter.outputs.shared }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
api:
- 'packages/api/**'
- 'packages/shared/**'
web:
- 'packages/web/**'
- 'packages/shared/**'
shared:
- 'packages/shared/**'
test-api:
needs: detect-changes
if: needs.detect-changes.outputs.api == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd packages/api && npm ci && npm test
test-web:
needs: detect-changes
if: needs.detect-changes.outputs.web == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd packages/web && npm ci && npm testCaching and Performance Optimization
Proper caching can cut CI time by 50-70%. Cache dependencies, build artifacts, Docker layers, and test fixtures. Use the GitHub Actions cache with proper keys that invalidate when dependencies change.
Key Takeaways
Advanced GitHub Actions CI/CD patterns — matrix builds, reusable workflows, OIDC auth, composite actions, and monorepo support — let you build maintainable pipelines that scale with your organization. Start with OIDC to eliminate static credentials, extract common patterns into reusable workflows, and implement path-based filtering for monorepos. The platform’s flexibility means you can implement virtually any CI/CD pattern without third-party tools.