GitHub Actions is a CI/CD platform built directly into GitHub, where every push, pull request, or scheduled event can trigger an automated workflow defined in YAML under `.github/workflows/`. A workflow is composed of one or more jobs, each of which is an independent unit of work that runs on a designated runner. By default, jobs run in parallel, but the `needs` keyword creates explicit dependencies so that, for example, a deploy job can wait for both build and test to complete first. Inside each job, steps execute sequentially and either run a shell command via `run:` or invoke a reusable action via `uses:`. Reusable actions can come from the GitHub Marketplace or from your own repositories, and a composite action lets you package several shell steps into a single reusable unit. The distinction between composite actions and reusable workflows matters: composites are step-level reuse, while reusable workflows invoked through `workflow_call` are whole-workflow reuse with their own jobs.
The runner is the server that actually executes a job. GitHub-hosted runners are managed virtual machines that are provisioned fresh for each job, available on Ubuntu, Windows, and macOS, and selected through the `runs-on:` key. Self-hosted runners are machines you operate yourself, giving you full control over the execution environment; they are valuable for specialized hardware, private network access, GPU workloads, or cost optimization at scale. Either flavor supports ephemeral mode — a runner that handles one job and then unregisters — which improves isolation. When maintaining self-hosted runners, the safe drain procedure is to remove the runner from the repository or organization, wait for any in-flight job to complete, and only then stop the runner service to avoid leaving a ghost entry in the UI.
Matrix builds amplify the value of a single job definition by running it across multiple combinations of variables such as operating system and language version. The `strategy.matrix` keyword defines those combinations, and GitHub automatically creates one job per cell. To control cost and feedback speed, `fail-fast: true` cancels pending matrix jobs when any one fails, while `max-parallel:` caps how many run concurrently. For monorepos or cases where the matrix depends on repository state, a dynamic matrix can be computed at runtime by emitting the matrix as an output from a setup job and consuming it in a downstream job. This pattern lets you build only the targets affected by a particular change.
Workflows respond to a rich set of events. `push` and `pull_request` are the daily drivers; `pull_request` runs in an isolated concurrency group so it does not affect a production deployment in flight. `workflow_dispatch` lets a human trigger the workflow from the GitHub UI, CLI, or REST API, optionally with typed input parameters, while `schedule` runs on a cron expression with a minimum interval of five minutes. `workflow_run` triggers one workflow after another finishes, enabling clean separation of CI and CD responsibilities with different permissions and approvers. For security, `pull_request_target` must be handled with care because it runs in the context of the base branch with access to secrets — any workflow that uses it must never echo attacker-controlled content. Path filters (`on.push.paths`) restrict runs to changes in specific directories, saving CI minutes in monorepos.
A few cross-cutting concepts give Actions its flexibility. Concurrency groups ensure that only one in-flight run per branch or workflow exists at a time, with `cancel-in-progress: true` aborting older superseded runs. `continue-on-error: true` allows a step or job to fail without halting the pipeline, useful for surfacing warnings without blocking merges. `timeout-minutes` enforces an upper bound on a job's wall-clock duration with a default of 360 minutes, and the workflow-run timeout is configurable up to 43,200 minutes as well. The automatically provisioned `secrets.GITHUB_TOKEN` carries the least-privilege scopes you grant in a `permissions:` block, and OIDC lets Actions assume short-lived cloud IAM roles without storing long-lived keys. Service containers provide sidecar dependencies such as a database for integration tests, and actions like `actions/checkout@v4` clone the repository with options like `fetch-depth: 0` for full git history when needed.