Skip to content

GitHub Actions

Scope

GitHub Actions CI/CD platform. Covers workflow configuration (.github/workflows YAML), event triggers (push, pull_request, schedule, workflow_dispatch, repository_dispatch), runner types (GitHub-hosted, larger runners, self-hosted), job execution (matrix builds, concurrency, environments), reusable workflows and composite actions, GitHub Container Registry (GHCR), GitHub Packages (npm, Maven, NuGet, RubyGems), Actions marketplace, OpenID Connect (OIDC) federation for cloud provider authentication, artifact and cache management, GitHub Environments with protection rules, and security hardening for workflows.

Checklist

  • [Critical] Design workflow structure with appropriate event triggers: use on: push and on: pull_request for CI, on: workflow_dispatch for manual triggers with input parameters, on: release for release automation; filter triggers with branch patterns (branches: [main, 'release/**']) and path filters (paths: ['src/**']) to avoid unnecessary workflow runs; use concurrency: groups with cancel-in-progress: true to prevent redundant runs on rapid pushes
  • [Critical] Implement OIDC federation for cloud authentication: configure cloud provider trust policies (AWS IAM Identity Provider, Azure federated credentials, GCP Workload Identity Federation) to accept GitHub's OIDC tokens; use permissions: id-token: write in workflows; replace long-lived cloud credentials stored as secrets with short-lived OIDC tokens; restrict trust to specific repositories, branches, and environments to prevent cross-repository token abuse
  • [Critical] Secure workflow execution and secret management: set minimum permissions: at workflow and job level (default contents: read only); pin third-party actions to full commit SHA (not tags which can be reassigned); never use pull_request_target with actions/checkout of PR head (pwn request vulnerability); store secrets at repository, environment, or organization level; use environment protection rules with required reviewers for production secrets
  • [Critical] Configure self-hosted runners securely: never use self-hosted runners on public repositories (any PR can execute arbitrary code); register runners at organization level with runner groups to control repository access; use ephemeral runners (--ephemeral flag) for clean environments per job; run runners in containers or VMs with no persistent state; restrict runner labels to specific workflows; keep runner software updated for security patches
  • [Recommended] Implement reusable workflows for organizational standards: create reusable workflows (on: workflow_call) in a central .github repository for standard CI/CD patterns (build, test, deploy, security scan); accept inputs and secrets parameters; set required_workflows at the organization level (Enterprise feature) to enforce mandatory checks; version reusable workflows with tags or branch refs for stability
  • [Recommended] Configure GitHub Environments with deployment protection: create environments (development, staging, production) with environment-specific secrets and variables; add protection rules -- required reviewers, wait timers, branch restrictions; use environment: in jobs to gate deployments; environment protection rules are the primary mechanism for deployment approval workflows; audit deployment history through the Environments UI
  • [Recommended] Optimize build performance with caching and artifacts: use actions/cache with hash-based keys (hashFiles('**/package-lock.json')) for dependency caching (npm, pip, Maven, Gradle); configure cache size within the 10 GB per repository limit; use actions/upload-artifact and actions/download-artifact for passing build outputs between jobs; set retention periods on artifacts to manage storage costs; consider larger runners for compute-intensive builds
  • [Recommended] Set up GitHub Container Registry (GHCR) for container images: push images to ghcr.io/OWNER/IMAGE using GITHUB_TOKEN authentication (no PAT required); configure package visibility (public or private, inheriting from repository by default); use docker/build-push-action with layer caching for efficient builds; set up automatic cleanup of untagged images; link packages to repositories for unified permissions management
  • [Recommended] Implement matrix builds for multi-platform testing: use strategy: matrix: to test across OS versions (ubuntu-latest, windows-latest, macos-latest), language versions, and dependency combinations; use include: and exclude: for targeted combinations; set fail-fast: false to run all combinations even if one fails; use max-parallel: to limit concurrency and manage runner costs
  • [Recommended] Design composite actions for reusable build steps: create composite actions (using: composite) for multi-step logic that is shared across workflows (setup toolchain, run linters, deploy to environment); publish to internal or public repositories; prefer composite actions over JavaScript/Docker actions when the logic is just shell commands; use inputs: and outputs: for parameterization
  • [Optional] Configure GitHub Packages for non-container artifacts: publish npm packages to GitHub Packages registry (npm.pkg.github.com), Maven artifacts to GitHub Packages Maven registry, NuGet packages, or RubyGems; configure .npmrc or settings.xml with GITHUB_TOKEN authentication; useful for internal package sharing within an organization; evaluate against dedicated package managers (Artifactory, CodeArtifact) for advanced features
  • [Optional] Implement workflow monitoring and cost management: use the GitHub Actions usage report (organization Settings > Billing) to track runner minutes consumption; set spending limits to prevent unexpected costs; monitor workflow run duration trends; use timeout-minutes: on jobs to prevent hung workflows from consuming minutes; consider larger runners with per-minute billing for workflows that benefit from more CPU/memory
  • [Optional] Evaluate GitHub Actions Immutable Actions (GHES) and verified creators: prefer actions from verified creators (blue checkmark) on the Actions Marketplace; for GitHub Enterprise Server, consider enabling the Actions allow list to restrict which actions can be used; pin actions to commit SHAs and use Dependabot to automatically propose SHA updates when new action versions are released

Why This Matters

GitHub Actions is the most widely adopted CI/CD platform for open-source and a rapidly growing choice for enterprise workflows. Its tight integration with GitHub pull requests, branch protection rules, and the extensive marketplace of community-contributed actions accelerate pipeline development. However, this convenience creates security risks that are frequently underestimated.

The most critical security concern is third-party action supply chain attacks. Actions referenced by mutable tag (uses: actions/checkout@v4) can be silently modified by the action maintainer or an attacker who compromises the repository. A compromised action runs with the permissions of the workflow and has access to all secrets passed to the job. Pinning actions to full commit SHAs (uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11) and using Dependabot for automated updates is the only reliable mitigation.

OIDC federation has fundamentally changed how GitHub Actions authenticates to cloud providers. Before OIDC, organizations stored long-lived cloud credentials (AWS access keys, Azure service principal secrets) as GitHub Secrets -- credentials that could be exfiltrated by malicious actions and never expired. OIDC tokens are short-lived (valid for the duration of the job), scoped to a specific repository and branch, and leave no persistent credentials to rotate. Organizations that have not migrated to OIDC should prioritize this as a security improvement.

Self-hosted runner security is a common gap. On public repositories, any user who opens a pull request can trigger workflow execution on self-hosted runners, gaining code execution on the runner host. This is why GitHub explicitly warns against using self-hosted runners on public repositories. Even on private repositories, runners must be ephemeral (destroyed after each job) to prevent one workflow from poisoning the environment for subsequent workflows. Runner groups at the organization level provide the access control mechanism for restricting which repositories can use which runners.

Common Decisions (ADR Triggers)

  • GitHub Actions vs GitLab CI/CD vs Jenkins -- GitHub Actions provides the richest marketplace ecosystem, native GitHub integration (PR checks, deployments, environments), and OIDC federation. GitLab CI/CD provides integrated security scanning and a single-platform DevSecOps experience. Jenkins provides maximum customization with thousands of plugins but requires infrastructure management. Use GitHub Actions for GitHub-native teams wanting rapid pipeline development. Use GitLab CI/CD for integrated DevSecOps. Use Jenkins for legacy pipelines or highly customized build environments.
  • GitHub-hosted runners vs self-hosted runners -- GitHub-hosted runners are fully managed with zero infrastructure, offer multiple OS options, and include 2000+ minutes/month on Team plan. Self-hosted runners provide custom hardware, persistent caches, VPC network access, and no per-minute billing. Use GitHub-hosted runners for standard builds without private network requirements. Use self-hosted runners for builds requiring private resource access, specialized hardware (GPUs, ARM), or high-volume pipelines where per-minute billing is cost-prohibitive.
  • GitHub-hosted larger runners vs standard runners -- Larger runners (4-64 vCPU, up to 256 GB RAM) provide faster builds for compute-intensive workloads with per-minute billing. Standard runners (2 vCPU, 7 GB RAM) are included in plan minutes. Use larger runners for compilation-heavy projects (C++, Rust, large Java projects) and parallel test suites. Use standard runners for typical web application builds, linting, and lightweight tests.
  • Reusable workflows vs composite actions -- Reusable workflows (workflow_call) define complete jobs with runner selection and can be required at the organization level. Composite actions define steps within a job and run on the caller's runner. Use reusable workflows for standardized CI/CD pipelines that should be consistent across repositories. Use composite actions for shared build steps (setup toolchain, run tests, deploy) that are composed within existing workflows.
  • GHCR vs Docker Hub vs ECR/ACR/GAR -- GHCR provides native GitHub integration, GITHUB_TOKEN authentication, and unified permissions with repositories. Docker Hub has the largest public image library and free public image hosting. Cloud registries (ECR, ACR, GAR) provide native integration with their respective cloud platforms. Use GHCR for GitHub-centric teams building and deploying containers. Use cloud registries when deploying to a specific cloud provider. Use Docker Hub for public open-source images.
  • Monorepo path filters vs polyrepo separate workflows -- Path filters (on: push: paths:) trigger workflows only when specific directories change, enabling monorepo CI without running all tests on every push. Polyrepo provides natural workflow isolation. Use path filters for monorepos with independent components. Use polyrepo workflows for teams wanting fully independent release cycles and simpler workflow configuration.

See Also

  • general/ci-cd.md -- general CI/CD pipeline patterns, platform comparison, branching strategies
  • general/deployment.md -- deployment strategies (rolling, blue-green, canary) and rollback procedures
  • providers/gitlab/ci-cd.md -- GitLab CI/CD for comparison with GitHub Actions
  • providers/aws/devops.md -- AWS DevOps services including OIDC federation target for GitHub Actions
  • providers/azure/devops.md -- Azure DevOps including OIDC federation target for GitHub Actions
  • providers/argocd/gitops.md -- ArgoCD GitOps for Kubernetes deployment triggered by GitHub Actions