GitHub Actions
GitHub Actions runs arbitrary automation on GitHub-hosted or self-hosted runners when events occur in your repository. It is YAML-first, integrates with the GitHub permission model, and scales from “run tests on PR” to multi-environment continuous delivery.
This post goes beyond a hello-world workflow: matrices, caching, reusable workflows, and security basics.
Anatomy of a workflow
name: CI
on:
push:
branches: [main]
pull_request:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install
run: npm ci
- name: Unit tests
run: npm test -- --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
Highlights
concurrency: cancels outdated runs on the same branch—saves minutes when people push rapidly.timeout-minutes: prevents hung jobs from burning quota indefinitely.npm ci: deterministic installs from lockfile—prefer overnpm installin CI.
Matrix builds
Test across Node versions or OS targets:
strategy:
matrix:
node: [18, 20, 22]
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
Use fail-fast: false when you want every cell to finish even after one fails (useful for compatibility matrices).
Reusable workflows
Extract common pipelines to .github/workflows/reusable-ci.yml with workflow_call triggers, then call from app repos with uses: org/repo/.github/workflows/reusable-ci.yml@main. Centralizes Node version, lint commands, and artifact retention policies.
Caching beyond setup-node
For monorepos with Turborepo or Nx, cache remote artifacts:
- uses: actions/cache@v4
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-
Tune keys when cache poisoning is a concern (rare for build artifacts, more relevant for sensitive outputs).
Secrets and environments
- Store long-lived tokens as repository or organization secrets.
- Use environments with required reviewers for production deploy jobs.
- Prefer OIDC federation to AWS/GCP/Azure over copying static cloud keys into GitHub when possible—short-lived tokens per job.
Never echo secrets to logs; GitHub masks known patterns but custom formats can leak.
Self-hosted runners
Use when you need GPUs, private network access, or unusual dependencies. Harden machines: auto-update, single-purpose runners, network segmentation—runners execute untrusted PR code.
Debugging
- Re-run jobs with debug logging enabled.
- SSH-like debugging:
tmateaction for stuck steps (use sparingly, security implications).
Summary
Actions shines when workflows are small, fast, and idempotent: concurrency control, deterministic installs, matrices for coverage, reusable workflows for consistency, and modern secret patterns (OIDC). Treat YAML as code—review changes like application logic.