GitHub Actions Advanced â Matrix Builds, Caching, Reusable Workflows, and Deployment Patterns
In this tutorial, you'll learn about GitHub Actions Advanced. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
GitHub Actions advanced patterns enable teams to build efficient, maintainable CI/CD pipelines using matrix strategies for parallel testing, intelligent caching to reduce build times, reusable workflows to eliminate duplication, and OIDC-based cloud authentication for secure deployments.
What You'll Learn
Why It Matters
Basic GitHub Actions workflows copy-pasted across repositories lead to configuration drift, wasted CI minutes from redundant builds, and security risks from long-lived cloud credentials stored as secrets. Advanced patterns reduce pipeline runtime by 60%, eliminate duplicate workflow code across dozens of repositories, and replace static cloud credentials with short-lived OIDC tokens that auto-rotate. These patterns are essential for organizations scaling from one Repository to hundreds.
Real-World Use
DodaTech maintains a single reusable deployment workflow consumed by 45 repositories. Matrix builds test Durga Antivirus Pro components across 3 operating systems and 5 Python versions in under 12 minutes using intelligent caching. OIDC authentication to AWS eliminates manual credential rotation for 200+ deployment environments.
flowchart TD
A["Caller Workflow"] -->|"uses: org/deploy-workflow@v1"| B["Reusable Workflow"]
B --> C["Matrix: OS x Python"]
C --> D["Ubuntu / Python 3.11"]
C --> E["Ubuntu / Python 3.12"]
C --> F["macOS / Python 3.11"]
C --> G["Windows / Python 3.11"]
D --> H["Restore Cache"]
H --> I["Run Tests"]
I --> J["Upload Artifacts"]
J --> K["Build Docker Image"]
K --> L["Scan + Push"]
L --> M["Deploy to Environment"]
M --> N["production"]
M --> O["staging"]
style A fill:#2088FF,color:#fff
style B fill:#2088FF,color:#fff
style C fill:#326CE5,color:#fff
Prerequisites: Working knowledge of GitHub Actions (jobs, steps, triggers), a GitHub repository, and basic Docker and CI/CD concepts.
Matrix Builds for Parallel Testing
Matrix builds test across multiple operating systems, language versions, and dependency configurations simultaneously.
# .github/workflows/matrix-test.yml
name: Matrix Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12"]
exclude:
- os: windows-latest
python-version: "3.10"
include:
- os: ubuntu-latest
python-version: "3.12"
coverage: true
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -r requirements-dev.txt
- name: Run tests
run: pytest tests/ -v --junitxml=results.xml
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.os }}-${{ matrix.python-version }}
path: results.xml
Expected behavior: GitHub Actions creates 8 parallel jobs (3 OS x 3 Python minus 1 exclusion plus 1 inclusion with coverage flag). The exclude block skips Windows + Python 3.10. The include block adds a coverage: true variable to the Ubuntu 3.12 job for running coverage reports.
Intelligent Caching Strategies
Cache Python packages, Docker layers, and Node modules to reduce workflow execution time.
# .github/workflows/caching.yml
name: Build with Caching
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache Python packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-docker-${{ hashFiles('Dockerfile') }}-${{ hashFiles('requirements*.txt') }}
restore-keys: |
${{ runner.os }}-docker-
- name: Install dependencies
run: pip install -r requirements.txt
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
tags: myapp:latest
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
Expected behavior: The cache key uses a hash of the dependency files. If requirements.txt has not changed, the cache is restored and pip install takes seconds instead of minutes. Docker caching preserves build layers between runs, so unchanged layers are reused instead of rebuilt.
| Caching Strategy | Key Pattern | Restore Keys | Typical Savings |
|---|---|---|---|
| Minimal | Hash of lockfile only | None | 30-50% |
| Fallback | Hash of lockfile | Partial key prefix | 50-70% |
| Segmented | Hash per dependency type | Branch-level key | 60-80% |
Reusable Workflows
Define a workflow once and call it from any number of repositories, eliminating CI/CD code duplication.
# .github/workflows/deploy-reusable.yml (called workflow)
name: Deploy Application
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
secrets:
AWS_ROLE_ARN:
required: true
outputs:
deployment-url:
description: "The URL of the deployed application"
value: ${{ jobs.deploy.outputs.url }}
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
permissions:
id-token: write
contents: read
outputs:
url: ${{ steps.deploy-app.outputs.url }}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
role-session-name: github-deploy-${{ inputs.environment }}
- name: Deploy to ECS
id: deploy-app
run: |
aws ecs update-service \
--cluster ${{ inputs.environment }}-cluster \
--service api-service \
--force-new-deployment \
--image ${{ secrets.ECR_REPOSITORY }}:${{ inputs.image-tag }}
echo "url=https://${{ inputs.environment }}.api.dodatech.com" >> $GITHUB_OUTPUT
- name: Verify deployment
run: |
aws ecs wait services-stable \
--cluster ${{ inputs.environment }}-cluster \
--services api-service
# .github/workflows/pipeline.yml (caller workflow)
name: Full Pipeline
on:
push:
branches: [main]
jobs:
# ... test and build jobs omitted for brevity ...
deploy-staging:
needs: [build]
uses: dodatech/deploy-actions/.github/workflows/deploy-reusable.yml@v1
with:
environment: staging
image-tag: ${{ github.sha }}
secrets:
AWS_ROLE_ARN: ${{ secrets.AWS_STAGING_ROLE_ARN }}
deploy-production:
needs: [build, deploy-staging]
uses: dodatech/deploy-actions/.github/workflows/deploy-reusable.yml@v1
with:
environment: production
image-tag: ${{ github.sha }}
secrets:
AWS_ROLE_ARN: ${{ secrets.AWS_PROD_ROLE_ARN }}
environment: production
OIDC Authentication for Cloud Providers
Replace long-lived cloud credentials with short-lived tokens using OpenID Connect.
# .github/workflows/oidc-auth.yml
name: OIDC AWS Deployment
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubOIDCRole
aws-region: us-east-1
- name: List S3 buckets (test access)
run: aws s3 ls
Expected behavior: GitHub requests a token from AWS using OIDC. The token is valid for 15 minutes and is tied to the specific workflow run. No static AWS access keys are stored anywhere. AWS IAM trusts the GitHub OIDC provider and maps it to an IAM role with least-privilege permissions.
Common Errors
Cache key too specific (includes branch name): A cache key like
${{ runner.os }}-${{ github.ref }}-${{ hashFiles('**/lockfile') }}creates one cache per branch, defeating the purpose. Userestore-keyswith the runner OS prefix to fall back to the default branch's cache.Matrix explosion: A matrix with 5 OS, 4 Python versions, and 3 dependency configs creates 60 jobs. GitHub Actions has a limit of 256 jobs per workflow. Use
exclude,include, andmax-parallelto keep the matrix manageable.Reusable workflow versioning without tags: Referencing a reusable workflow with
@mainmeans every workflow run uses the latest code from the main branch, which could change without notice. Pin to a Git tag (@v1) or commit SHA (@abc123).Secrets not accessible in reusable workflows: Reusable workflows receive secrets only through the
secrets: inheritkeyword or explicit mapping. Directly accessing${{ secrets.MY_SECRET }}in a called workflow without passing it results in an empty value.Environment protection rules blocking deployments: Deployment jobs targeting an environment with
required_reviewersorwait_timerprotection rules are blocked until an approver approves or the timer expires. Without this awareness, teams are confused why deployments hang for 30 minutes.
Practice Questions
How does the matrix strategy
includekeyword differ from directly listing combinations? Answer:includeadds new combinations to the matrix that are not generated by the Cartesian product. It is also used to add additional variables to specific existing combinations (likecoverage: truefor the Ubuntu 3.12 job). Direct listing requires enumerating every combination explicitly.Why is OIDC authentication more secure than storing AWS keys as secrets? Answer: OIDC tokens are short-lived (15 minutes), tied to a specific workflow run, and auto-rotate without manual rotation. Stored secrets are long-lived, can be leaked through logs or artifact uploads, and require manual rotation that is often forgotten.
What happens when a reusable workflow needs to return data to the caller? Answer: Reusable workflows use
outputsdefined at the job level and mapped at theworkflow_calllevel. The caller accesses them vianeeds.<job_id>.outputs.<output_name>. Outputs must be strings -- objects and arrays are not supported.How does Docker layer caching interact with BuildKit's
--cache-fromand--cache-to? Answer:cache-toexports the final build cache to a location (local filesystem, registry, S3) after the build.cache-fromimports a previously exported cache before the build. Themode=maxparameter saves all layers (not just the final stage), enabling maximum reuse across builds.
Challenge
Build a complete CI/CD system using advanced GitHub Actions patterns: create a reusable workflow deploy-app.yml that accepts environment, image-tag, and region inputs and uses OIDC authentication to deploy to AWS ECS Fargate with zero-downtime deployments, implement a caller workflow in another Repository that uses a matrix strategy to build Docker images for 3 services (api, worker, frontend) with parallel test execution across Node 18, 20, and 22, cache node_modules using the actions/cache action with a hash of package-lock.json, and chain the jobs so tests pass before building, building succeeds before deploying to staging, and staging passes before deploying to production with manual approval.
Mini Project
Build a reusable GitHub Actions platform for an organization with 20+ Microservices: create two reusable workflows (ci.yml for test/build, cd.yml for deploy), define a matrix strategy that covers Node.js 18/20/22 and 3 operating systems with exclusions for unsupported combinations, implement Docker layer caching with BuildKit that reduces build times by 70% across runs, configure OIDC authentication for deployment to AWS and GCP, add deployment verification steps (smoke tests, canary analysis), integrate status checks with deployment environments (staging approval, production approval with wait timer), create a composite action for setting up the monorepo tooling that is shared across workflows, publish the reusable workflows to a central .github Repository with semantic version tags, and write comprehensive documentation with Migration guides for teams to adopt the shared workflows.
Related Resources
| Resource | Description |
|---|---|
| GitHub Actions Basics | Foundation workflows |
| CI/CD Best Practices | Pipeline Design Patterns |
| Docker Multistage Builds | Optimizing Docker builds |
| DevSecOps Pipeline | Security scanning in CI/CD |
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro