GitHub Actions CI best practices
Practical, copyable GitHub Actions guidance for faster, leaner, cheaper CI and safer workflows. Optimize caching, sharding, scoped builds, path filters, concurrency, right-sized runners, readiness, timeouts, and queue time; then protect permissions, secrets, pull request triggers, caches, action identity, and OIDC. One reference page each, with good and bad YAML and how to verify it on your own repo.
18 practices in 6 categories
Install the free /ci-score skill to improve your GitHub Actions setupCaching & Setup
GitHub Actions cache: dependencies, keys, and cache hits
The GitHub Actions cache reuses package-manager downloads between workflow runs.
Set actions/checkout fetch-depth: 0 vs 1 vs 2 in GitHub Actions
Speed up GitHub Actions checkout and reduce network transfer by letting
actions/checkoutfetch only the commit under test (its defaultfetch-depth: 1) instead of the whole git history.
Parallelization
Shard tests across parallel jobs in GitHub Actions
Speed up GitHub Actions tests by splitting one long test job into parallel shards with a matrix, so the suite finishes in a fraction of the wall-clock time.
Build and test only what changed in GitHub Actions
Speed up GitHub Actions and cut runner minutes by scoping your slowest build/test job to only the packages a PR actually changed, using your monorepo tool's affected mode, with a mandatory full-run fallback so a resolution error never silently skips work.
Trigger Scope
Filter workflows with paths and paths-ignore in GitHub Actions
Add
pathsorpaths-ignoreto expensive workflows so a docs-only or unrelated change doesn't trigger the full test suite.Cancel superseded runs with concurrency and cancel-in-progress
Add a GitHub Actions
concurrencygroup scoped to the ref and turn oncancel-in-progressfor PR runs (it defaults tofalse), so a new push cancels the now-obsolete run instead of leaving both to occupy runners.
Required-check Hygiene
Runner & Queue
Cut CI queue time in GitHub Actions
GitHub-hosted runners cap how many jobs your account can run at once (20 on Free, more on paid plans), so past that ceiling jobs queue for a free slot no matter how your workflows are written.
Right-size GitHub Actions runners without slowing CI
Right-size a GitHub Actions job by comparing the same commit on the current and smaller runner, then keep the smaller size only when pass rate and wall-clock remain equivalent.
Bound GitHub Actions runner jobs with timeout-minutes
Set
timeout-minuteson every GitHub Actions job that executes on a runner, plus hang-prone steps, so a stuck run is cancelled in minutes instead of occupying a runner for up to the 360-minute default.Replace fixed CI sleeps with bounded readiness polling
Poll the exact condition a test needs, stop as soon as it is true, and cap the wait with a diagnostic failure so fast runs do not pay a worst-case sleep and slow runs do not become flaky.
Wait for container healthchecks instead of sleeping
Give every service container a healthcheck that proves it can answer a real request, then start the stack with
docker compose up -d --waitso the job blocks exactly until each service is ready.
Security
GitHub Actions permissions: use least privilege
Set a read-only top-level
permissions:default in every GitHub Actions workflow, then grant each job only the additionalGITHUB_TOKENscopes it needs, so unrelated steps cannot inherit repository write access.GitHub Actions secrets: prevent leaks and overexposure
Expose a GitHub Actions secret only to the step that consumes it, gate production secrets with an environment, prefer short-lived OIDC credentials, and keep credentials out of logs, command arguments, caches, and artifacts.
Secure pull_request_target in GitHub Actions
Use
pull_requestfor any job that checks out, builds, installs, or tests contributor code; reservepull_request_targetfor metadata-only work on trusted base-branch code with minimal permissions.Prevent GitHub Actions cache poisoning
Keep untrusted code from writing cache entries that trusted release or deploy jobs restore, and cache inert dependency data rather than executable workspace state or credentials.
Pin GitHub Actions to commit SHAs
Reference every third-party action by its full 40-character commit SHA (with the version as a trailing comment), not a mutable
@v4tag or@mainbranch that its maintainer, or an attacker, can re-point.Scope id-token: write to the publishing job
In GitHub Actions, declare
id-token: writein the specific publishing job'spermissions:block, never at the workflow's top level, so an OIDC publish token isn't minted for jobs that run untrusted code.
Hand your whole CI to an agent
Paste this into your coding agent (Claude Code, Cursor, and the like) to audit your repo against every practice above and open a reviewable PR for each gap.
Audit this repository's GitHub Actions CI against StarSling's best-practices catalog and fix what's missing. Start by reading the machine-readable index at https://starsling.dev/best-practices/github-actions.md, then read each per-practice page it links (at https://starsling.dev/best-practices/github-actions/{slug}.md) so you have the full guidance for every practice. Go through this repo's .github/workflows plus CI-adjacent test and integration source, and check them against each practice: dependency caching, shallow checkout, test sharding, building and testing only what changed, path filters, cancelling superseded runs with concurrency groups, keeping advisory checks off the critical path, queue time, right-sizing runners from measured job behavior, replacing fixed test sleeps with bounded readiness polling, waiting on container healthchecks instead of sleeping for service startup, bounding every runner-executing job with timeout-minutes, least-privilege GITHUB_TOKEN permissions, scoped and gated secrets, safe pull_request_target usage, cache-poisoning prevention, pinning actions to commit SHAs, and scoping id-token write to the publishing job. For every gap, apply the fix following that practice's page and respect its guardrails (for example, keep a full-run fallback when scoping to changed files, never de-scope a check that runs real tests, and never execute fork code in a privileged workflow). Show me the diff and open a PR for each change rather than applying anything blindly, and skip any practice that genuinely does not apply to this repo, noting why.Go further
One fix, all of them, or forever.
You have the prompt for the whole catalog. Here is how much further you can take it, each step doing more for you than the last.
Start by hand
Copy the whole-catalog prompt above
Hand the whole-catalog prompt above to your coding agent and clear every gap in your repo today.
Fix everything, once
Install the ci-speedup skill
One prompt audits your whole repo against all 73 ci-speedup patterns and hands your agent every fix at once. Open source, MIT, runs locally.
Keep it fixed, forever
Install the StarSling GitHub App
Connect GitHub and the fixes stay applied as your CI evolves, with agents that keep inspecting your workflows and opening optimization PRs you review.
FAQ
How do I optimize GitHub Actions?
Work the pipeline in the order the time is actually spent. First cut the wait before a job starts: scope each concurrency group to ${{ github.ref }} so unrelated PRs don't serialize, and cancel superseded PR runs with cancel-in-progress. Then stop doing work you don't need: paths filters (or a job-level if: when the check is required) keep a docs-only change from spinning up the suite, actions/checkout runs at its default fetch-depth: 1 instead of cloning the whole history, and in a monorepo the long-pole job builds and tests only what the diff affected (with a full-run fallback). Then make the remaining work faster: cache the dependency store keyed on the lockfile (cache: on actions/setup-node / setup-python / setup-go, or actions/cache keyed on hashFiles(...)), and shard the slowest test job across a matrix so wall-clock drops toward total/N. Finally, keep it safe: pin every action to a full-length commit SHA and grant id-token: write only to the job that publishes. Each practice has its own page with the good and bad YAML side by side.
What are the most important GitHub Actions CI best practices?
The highest-impact ones are caching dependencies so installs don't repeat every run, sharding long test suites across parallel jobs, scoping expensive workflows with path filters, cancelling superseded runs with a concurrency group, and hardening security by pinning actions to commit SHAs. Each has its own page here with copyable YAML.
How is this catalog organized?
Practices are grouped into the six categories the StarSling CI Score uses: Caching & Setup, Parallelization, Trigger Scope, Required-check Hygiene, Runner & Queue, and Security. Every page shows what good looks like, a good/bad YAML pair, and how to verify the practice on your own repo.
Do I have to use StarSling to apply these?
No. Every practice is a change you can make to your own GitHub Actions workflows by hand, and each page includes the verify steps to check it. StarSling's agents automate the same fixes by opening reviewable PRs, but the practices stand on their own.
More ways to improve GitHub Actions
If you do not know why a run is slow yet, start with the diagnostic guide. Let an agent find which of these applies: /ci-speedup. If your workflows are already optimized but still slow, see our guide to fast GitHub Actions and GitHub Actions runner alternatives. Building containers in CI? The Docker workflow guide covers layer caching end to end. Want to see how your configuration measures up before changing anything? CI Score grades workflow config against a pass/fail rubric of these practices. It is not a speed measurement. To find exploitable workflow paths before attackers do, ci-secure checks the ten critical GitHub Actions attack vectors and lets you choose which fixes to apply.
- GitHub Actions too slow
- Fast GitHub Actions
- GitHub Actions alternatives
- GitHub Actions pricing
- Self-hosted GitHub Actions runners
- Docker CI on GitHub Actions
- Install the free /ci-score skill to improve your GitHub Actions setup
- Install the /ci-secure skill to close critical attack vectors in GitHub Actions
- Browse GitHub Actions agent skills
References
- StarSling docs (opens in new tab)
- CI optimization docs (opens in new tab)
- All StarSling comparisons
- ci-speedup skill on GitHub (open source, MIT) (opens in new tab)
- ci-score skill on GitHub (open source, MIT) (opens in new tab)
- ci-secure skill on GitHub (open source, MIT) (opens in new tab)
- llms.txt (opens in new tab)
Every practice on this page, enforced in your repo.
One line to install. Faster runs on day one, and agents that open reviewable PRs to keep your GitHub Actions pipeline following these practices.
Last updated 2026-08-19