GitHub Actions cheat sheet: triggers, matrix, secrets, caching
The GitHub Actions syntax you reach for most, in copy-ready blocks: event triggers and filters, build matrices, secrets and permissions, dependency caching and artifacts, concurrency, job outputs and conditions.
Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?
Workflows live in .github/workflows/*.yml. Each has triggers (on), one or more jobs that run on separate runners in parallel unless linked with needs, and each job has steps that run in sequence on one runner. Everything below is current syntax; pin third-party actions to a major version (@v4) or, for stricter supply-chain control, to a full commit SHA.
Triggers
on:
push:
branches: [main, "release/**"]
tags: ["v*"]
paths-ignore: ["docs/**", "*.md"]
pull_request:
types: [opened, synchronize, reopened]
schedule:
- cron: "30 2 * * 1-5"
workflow_dispatch:
inputs:
environment:
description: Target environment
type: choice
options: [staging, production]
default: staging
workflow_call:
branches,tags,pathsand their-ignorevariants filterpushandpull_request; globs like**are allowed.scheduleuses POSIX cron in UTC and only runs on the default branch. Scheduled workflows are disabled after 60 days without repository activity.workflow_dispatchadds a Run workflow button; inputs are read as${{ inputs.environment }}.workflow_callmakes the workflow reusable from others withuses: owner/repo/.github/workflows/name.yml@main.pull_request_targetruns in the context of the base branch with write permissions; never check out and execute code from the pull request under it.
Matrix
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 4
matrix:
os: [ubuntu-latest, windows-latest]
node: [20, 22]
include:
- os: ubuntu-latest
node: 24
experimental: true
exclude:
- os: windows-latest
node: 20
continue-on-error: ${{ matrix.experimental == true }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm test
- Every combination of the lists runs as its own job;
includeadds extra combinations or extra keys,excluderemoves some. fail-fast: falsekeeps the other jobs running after one fails; the default cancels them.continue-on-errorat job level marks the job as passed even if it fails, useful for experimental versions.
Secrets, variables and permissions
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v5
- name: Deploy
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./deploy.sh
- Secrets are set under Settings > Secrets and variables > Actions at repository, environment or organization level and read as
${{ secrets.NAME }}; they are masked in logs. Non-secret settings go in Variables and are read as${{ vars.NAME }}. - Pass secrets to commands through
env, not by splicing them intorunstrings, so they do not appear in the shell trace. secrets.GITHUB_TOKENis created per run. The top-levelpermissionsblock sets its scopes for every job; a job-level block overrides it. Start withcontents: readand add only what a job needs.id-token: writeenables OpenID Connect so cloud providers can trust the run without long-lived keys.environment: productionbinds the job to an environment with its own secrets, required reviewers and deployment branch rules.- Secrets are not passed to workflows triggered by
pull_requestfrom forks, and the token is read-only there.
Caching and artifacts
steps:
- uses: actions/checkout@v5
- uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-${{ runner.os }}-
- run: cargo test
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-report
path: target/report/
retention-days: 7
actions/cacherestorespathwhenkeymatches exactly and falls back to the newest entry matching arestore-keysprefix; the cache is saved at the end of the job if the exact key was not found. Caches are evicted after 7 days without use and the repository total is capped at 10 GB.hashFileshashes lock files so the key changes when dependencies change.- The
setup-node,setup-python,setup-goandsetup-javaactions have acacheinput that handles the package manager cache without a separate cache step. - Artifacts (
upload-artifactanddownload-artifact) move files between jobs in a run and keep them for download; caches are for speeding up future runs.
Concurrency, outputs, conditions and dependencies
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
version: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v5
- id: meta
run: echo "version=$(git describe --tags --always)" >> "$GITHUB_OUTPUT"
- run: echo "BUILD_DIR=dist" >> "$GITHUB_ENV"
- run: echo "### Built ${{ steps.meta.outputs.version }}" >> "$GITHUB_STEP_SUMMARY"
release:
needs: build
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- run: echo "releasing ${{ needs.build.outputs.version }}"
concurrencywithcancel-in-progressstops an older run of the same workflow on the same branch when a new push arrives.timeout-minuteskills a hung job; the default is 360 minutes.- Steps write outputs by appending
name=valueto$GITHUB_OUTPUT; jobs expose them underoutputsand dependants readneeds.JOB.outputs.NAME.$GITHUB_ENVsets environment variables for later steps,$GITHUB_STEP_SUMMARYrenders Markdown on the run page. needsorders jobs;ifaccepts expressions withgithub.event_name,github.ref,startsWith,contains,success(),failure()andalways().- Useful contexts:
github.sha,github.actor,github.repository,runner.os,runner.temp,steps.ID.outcome.
Services and containers
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_PASSWORD: test
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v5
- run: psql postgresql://postgres:test@localhost:5432/postgres -c 'select 1'
services starts side containers for the job and waits for their health checks; the job reaches them on localhost when it runs on the VM directly, or by service name when the job itself runs in a container.
Debugging
- Re-run a failed run with Re-run jobs > Enable debug logging, or set the repository secret
ACTIONS_STEP_DEBUGtotrue. - Print the whole event payload once with
run: echo '${{ toJSON(github.event) }}'to see which fields are available. - Run workflows locally with nektos/act before pushing.
gh run list,gh run view --logandgh run rerunfrom the GitHub CLI do the same as the web interface from a terminal.
Official documentation
Did this work for you?
No votes yet. Be the first.
If it did not work, the report form below tells the editors what went wrong.
See also
- 0039 A minimal GitHub Actions workflow that runs your tests Add one YAML file under .github/workflows and GitHub runs your tests on every push and pull request. Here is a small, explained… Intermediate · Step by step · Any system Article GitHub Actions 15 Oct 2026
- 0256 Manage infrastructure as code with Terraform: from first file to plans on pull requests Learn the full Terraform workflow without a cloud account: variables, a module, outputs, state, plan, apply and destroy, a backend for shared… Beginner · Guided project · Linux, macOS, Windows Article GitHub Actions · Terraform 15 Oct 2026
- 0379 Your first GitHub Actions workflow: run tests on every push Build a working GitHub Actions pipeline from nothing: a small Python project with a test, a workflow file that runs pytest on… Beginner · Guided project · Any system Article GitHub Actions · Pipelines, runners & hooks 15 Oct 2026
- 0382 Fix: Resource not accessible by integration and other GitHub Actions permission errors A workflow step fails with Resource not accessible by integration, 403 on git push, or a GitHub CLI call is refused. The… Intermediate · Fixes an error · Any system Article GitHub Actions 15 Oct 2026
Repos in the wild
- act: run your GitHub Actions workflows locally with Docker nektos/act The nektos/act repository: a command-line tool that reads .github/workflows, simulates GitHub events and runs the jobs in Docker containers on your machine, so you can test a workflow without pushing. GitHub
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- Dominik +7,565 chars