Skip to content

Reduce GitHub Actions costs through workflow optimizations #2089

@ScottWilliamAnderson

Description

@ScottWilliamAnderson

Summary

Optimize GitHub Actions workflows to reduce billable runner minutes while maintaining all existing functionality and leaving room for future enhancements.

Background

Current workflow analysis reveals several cost optimization opportunities:

  • Multiple workflows run on every PR regardless of what files changed
  • Playwright tests use 4 parallel shards (4× runner cost)
  • test-docker-compose duplicates testing already covered by Playwright
  • No Docker layer caching between workflow runs

Proposed Changes

1. Remove test-docker-compose from PRs (keep on master only)

Estimated savings: ~3-5 min per PR

Change the trigger from:

on:
  push:
    branches: [master]
  pull_request:
    types: [opened, synchronize]

to:

on:
  push:
    branches: [master]

Rationale: Playwright tests already validate the full Docker stack works. Running both is redundant for PRs.


2. Reduce Playwright shards from 4 to 2

Estimated savings: ~50% of Playwright costs

Update the matrix in playwright.yml:

strategy:
  fail-fast: false
  matrix:
    shardIndex: [1, 2]  # was [1, 2, 3, 4]
    shardTotal: [2]      # was [4]

Update artifact download pattern accordingly.

Tradeoff: Tests will take ~2x longer to complete, but cost half as much.


3. Add path filtering to lint-backend.yml

Estimated savings: Skip ~2-3 min on frontend-only PRs

Add a changes job similar to what playwright.yml already uses:

jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      backend: ${{ steps.filter.outputs.backend }}
    steps:
      - uses: actions/checkout@v5
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            backend:
              - backend/**
              - pyproject.toml
              - .github/workflows/lint-backend.yml

  lint-backend:
    needs: [changes]
    if: ${{ needs.changes.outputs.backend == 'true' }}
    # ... existing job config

Add an alls-green job for branch protection compatibility.


4. Add path filtering to test-backend.yml

Estimated savings: Skip ~5-8 min on frontend-only PRs

Same pattern as above:

filters: |
  backend:
    - backend/**
    - docker-compose*.yml
    - .github/workflows/test-backend.yml

5. Add path filtering to generate-client.yml

Estimated savings: Skip ~3-4 min when only frontend/docs change

Only run when backend API-related files change:

filters: |
  api:
    - backend/app/api/**
    - backend/app/models.py
    - backend/app/main.py
    - scripts/generate-client.sh
    - .github/workflows/generate-client.yml

6. (Optional/Future) Add Docker layer caching

Potential additional savings: 1-3 min per Docker build

Consider adding Docker layer caching using docker/build-push-action with GitHub Actions cache:

- uses: docker/build-push-action@v5
  with:
    context: .
    cache-from: type=gha
    cache-to: type=gha,mode=max

This is more complex to implement but would speed up Docker builds across all workflows.


Checklist

  • Update test-docker-compose.yml - remove PR trigger
  • Update playwright.yml - reduce to 2 shards
  • Update lint-backend.yml - add path filtering + alls-green job
  • Update test-backend.yml - add path filtering + alls-green job
  • Update generate-client.yml - add path filtering + alls-green job
  • Update branch protection rules if needed (require the new alls-green jobs)
  • (Optional) Add Docker layer caching to workflows

Estimated Impact

Scenario Current Minutes (est.) After Optimization (est.)
Backend-only PR ~35-45 min ~20-25 min
Frontend-only PR ~35-45 min ~10-15 min
Full-stack PR ~35-45 min ~25-30 min
Push to master ~40-50 min ~40-50 min (unchanged)

Note: Actual savings depend on your PR patterns. If most PRs touch both frontend and backend, savings will be closer to 30-40%. If many PRs are frontend-only or backend-only, savings could reach 60-70%.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions