GitHub Actions turned six this year, and what began as a clever automation toy is now the default CI/CD engine for millions of repositories. The marketplace passed 20 000 public actions, self‑hosted runners live on everything from Raspberry Pi clusters to AWS Graviton fleets, and GitHub’s new hosted macOS ARM runners slash iOS build times in half. Yet many pipelines still waste minutes on redundant steps, leak secrets into logs, or gobble compute minutes at enterprise scale.
That’s why you’re here—GitHub Actions CI/CD Cheat Sheet 2025: Ship Code Faster and Safer distills the best patterns, secrets‑management tricks, matrix strategies, caching hacks, and security guardrails into a single marathon guide. Read it, sprinkle the snippets into your workflows, and you’ll shave hours off release cycles without risking supply‑chain nightmares.
We’ll mention GitHub Actions CI/CD Cheat Sheet 2025: Ship Code Faster and Safer once more later (SEO loves repetition, so do skim‑readers). Let’s start.
How the Runner Ecosystem Evolved
Runner Tier | vCPU / RAM (2025 default) | OS images available | Key 2025 Upgrade |
---|---|---|---|
ubuntu‑24.04 | 2 vCPU / 7 GB | 24.04 (LTS), 22.04 | M1 cross‑compile toolchain |
windows‑2025 | 4 vCPU / 16 GB | Server 2025 Core | GPU drivers for CUDA 12 |
macos‑14-arm | 4 vCPU / 8 GB | Sonoma ARM | 50 % faster iOS builds |
Self‑Hosted | Unlimited | Any | Runner groups + required labels |
Large Runners (beta) | 16 vCPU / 64 GB | Ubuntu, Windows | Pay‑per‑second billing |
Price Primer (Hosted Runners)
- Public repos – free unlimited minutes
- Private repos – 3 000 min/month (Pro), 50 000 min/month (Team), enterprise by seat
- Additional Linux minute: $0.006 / Windows: $0.012 / macOS: $0.015
- Job cancellations before 60 seconds are free—use
if:
checks early to abort
Basics Refresher You Still Forget
- Top‑level keys:
name
,on
,permissions
,jobs
on
can be array or map; useworkflow_dispatch
with inputs for manual triggersruns-on
accepts arrays for labels:['self-hosted','gpu']
- Conditionals:
if: contains(github.event.head_commit.message, '[skip ci]') == false
- Default shell:
bash
on Linux/macOS,pwsh
on Windows unlessshell:
overrides - Secrets context:
${{ secrets.MY_SECRET }}
—never echo
H2 GitHub Actions CI/CD Cheat Sheet 2025: Ship Code Faster and Safer with Workflow Strategy
1. Split Monolithic Pipelines
Bad anti‑pattern: one giant workflow triggered by every event.
Better: three slim workflows:
pr.yml
– style, unit tests, lint; triggerspull_request
push.yml
– build artifact, integration tests; triggerspush
on mainrelease.yml
– semantic‑version tag push, deploy to prod
Faster feedback for PR authors; deploy logic stays isolated.
2. Use Reusable Workflows
Create .github/workflows/reusable-test.yml
:
yamlCopyon:
workflow_call:
inputs:
lang:
required: true
type: string
jobs:
run-tests:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-${{ inputs.lang }}@v1
- run: npm test
Then call in service repos:
yamlCopyjobs:
tests:
uses: org/.github/.github/workflows/reusable-test.yml@main
with:
lang: node
Centralizes logic; update once, profit everywhere.
3. Matrix Smarter, Not Harder
yamlCopystrategy:
fail-fast: false
matrix:
node: [18, 20]
os: [ubuntu-24.04, windows-2025]
include:
- node: 22
os: ubuntu-24.04
fail-fast: false
keeps other jobs alive when one axis fails.- Use
include
to cover edge combos without exploding permutations. - For 10+ jobs, enable
max-parallel
to respect minute quotas.
4. Job Dependencies
needs:
speeds early failure:
yamlCopyjobs:
lint:
...
build:
needs: lint
deploy:
needs: build
If lint
fails, build
and deploy
skip, saving minutes.

Artifact & Cache Tactics
Global Build Cache
yamlCopy- name: Cache node_modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-
- Keys hashed on lock file—cache busts only when deps change.
- Store Docker layers:
~/.cache/buildx
for Go, Rust, Node images.
Large Artifacts Management
- Use
actions/upload-artifact
max 10 GB per run, but consider TTL (90 days). - For bigger: push build outputs to S3 in workflow (assume‑role OIDC) rather than clogging artifact store.
- Compress logs:
tar cz
before upload; smaller retention cost.
Secrets & OIDC Federation
Moving Away from Long‑Lived PATs
GitHub’s OpenID Connect (OIDC) releases short‑lived tokens to cloud providers.
Example: AWS OIDC role:
yamlCopypermissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
role-to-assume: arn:aws:iam::123:role/GitHubOIDCRole
aws-region: ap-southeast-2
No static keys in secrets
; token expires in 1 hour.
Best‑Practice Secrets Storage
Use Case | Location | Why |
---|---|---|
Deployment tokens | Repo or Org Secrets | Scoped by environment |
Database passwords | Environment Secrets per env | Limit breach blast radius |
Tool configs (non‑secret) | settings.json committed | Version‑controlled, no secret rotation issues |
Never store secrets in vars
—they appear in logs on echo.
Security Guardrails
permissions: read-all
is old; setpermissions: { contents: read }
minimum and elevate per job.- Use
actions/checkout@v4 fetch-depth: 0
only when you need git history; shallow clones are faster and safer. - Dependabot +
npm audit
step in CI catches CVEs quickly. - Block
fork
workflows from accessingsecrets
—setsecrets: inherit
only when needed. - Enable required reviewers on workflow file changes; prevents malicious PR altering pipeline.
Monitoring & Cost Visibility
Usage Insights Dashboard
- Shows minutes, storage, data transfer per repo/org.
- Set budget alerts: get email when minutes cross 80 % of plan.
- Large Repo feature compresses LFS by default.
Log Retention Strategy
- Default log retention: 30 days (public), 90 days (Enterprise).
- Use:
yamlCopyjobs.<job_id>.timeout-minutes: 20
to kill hung tests—saves minutes and clutter.
Advanced Patterns
1. Docker‑in‑Docker with BuildKit
yamlCopyservices:
dind:
image: docker:dind
options: >-
--privileged
--pull=always
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: docker/setup-buildx-action@v3
- run: docker buildx build --file ./Dockerfile --tag myapp:pr-${{ github.sha }} .
2. Monorepo Conditional Workflows
yamlCopyif: "!contains(github.event.head_commit.message, '[skip ci]') &&
github.event_name == 'push' &&
( github.event.modified.contains('api/') || github.event.added.contains('api/') )"
Build only changed package path.
3. Self‑Hosted GPU Runners
- Tag runners
['self-hosted','gpu']
; in workflow setruns-on: [self-hosted, gpu, x64]
. - Use
actions/runner-scale-set
for Azure VMSS auto‑scaling by queue length. - Encrypt registration token with environment secret; rotate monthly.
4. Large Runners for Monolithic Builds
Swap:
yamlCopyruns-on: ubuntu-24.04
for:
yamlCopyruns-on: ubuntu-latest
runner-type: large
16 vCPU speeds big C++ or Unreal Engine compiles; pay‑per‑second.
Common Pitfalls & Fixes
Problem | Quick Fix |
---|---|
“Waiting for runner to pick up job” | Too many jobs, set concurrency group or buy minutes; for open source, use jobs.<id>.runs-on: ubuntu-latest not specific version to increase capacity. |
Cache “Not Saved” | Key changed mid‑workflow; compute key before action steps. |
Node Modules Re‑install each run | Move install into deps job, upload artifact, download for test job. |
Secrets in Logs | Add ::add-mask::${{ secrets.PAT }} before commands that echo variables. |
Self‑Hosted Runner Idle Charges | Use autoscaling or GitHub’s scale down lambda example to shut instances after queue drains. |
GitHub Actions CI/CD Cheat Sheet 2025: Ship Code Faster and Safer—Performance Benchmarks
Optimization | Build Time ↓ | Minutes Saved/Month (1 500 builds) |
---|---|---|
Enable Node cache | 55 % | 400 |
Split tests across matrix 4× | 60 % | 500 |
Reusable workflow DRY | 10 % | 80 |
Self‑hosted ARM runners (Go build) | 35 % | 250 |
if: steps.changed-files.outputs... | 30 % | 180 |
Adopt three and watch your bill shrink.
FAQ
Can I test workflows locally?
Yes—use act
CLI for Linux Docker; partial Windows/macOS support via tags.
How do I debug secrets not found?
Secrets don’t pass to pull_request
from forks unless explicitly set. Use workflow_run
on target repo.
Are composite actions slower?
Negligible; they avoid container spin‑up and share runner context.
How many nested reusable workflows are allowed?
Up to four levels deep; keep two for sanity.
Does caching work on self‑hosted runners?
Yes—actions/cache
supports self‑hosted; ensure cache URL env var reachable.