Git, CI/CD & GitOpsXL · RunnersRunners
Runner tooling and actions — what is installed, GitHub-managed actions, and the marketplace
What you'll learn
- Identify the runtime that a runner image provides out of the box
- Distinguish GitHub-managed actions from first-party actions from third-party marketplace actions
- Pin actions by full-length commit SHA rather than by tag
- Recognise the supply-chain risk of arbitrary marketplace actions
- Decide when to use an action versus when to write the step inline
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A runner is the trust boundary for code. A workflow that says
uses: someorg/someaction@v1 is asking GitHub to clone the
someorg/someaction repository at the v1 tag and run its
entrypoint inside the workflow job. The action runs as the
runner user, with the runner’s secrets, on the runner’s host.
Picking an action is picking code to run.
Knowing which actions to trust, which to pin, and which to audit is the supply-chain half of CI security.
What the runner image provides
flowchart LR
IMG["Runner image"] --> OS["OS packages\n(apt / brew / choco)"]
IMG --> LANG["Languages\n(python, node, go, ruby)"]
IMG --> TOOLS["Tools\n(git, docker, jq, curl)"]
IMG --> RUNNER["Runner binary"]
IMG --> NULL["(no extra CI tooling)"]
The image provides the operating system, the language
runtimes, and the common tools. It does not provide
CI tooling. There is no pytest, no eslint, no
terraform baked in — those come from actions/setup-* or
from explicit pip install / npm install steps inside the
job. This is by design: the team owns what the workflow
needs.
| Tool | On hosted ubuntu-latest | On self-hosted |
|---|---|---|
git | Yes | Depends |
python3 | Yes | Depends |
node | Yes | Depends |
docker | Yes (rootless) | Team installs |
terraform | No | Team installs |
kubectl | No | Team installs |
Actions: three tiers
flowchart TB
T1["Tier 1: GitHub-managed\n(actions/*)"] --> SAFE["Highest trust:\nGitHub owns and patches"]
T2["Tier 2: First-party\n(github.com/*)"] --> SAFE2["Trusted:\nforge-owned, audited"]
T3["Tier 3: Marketplace\n(community)"] --> RISK["Lowest trust:\nanyone can publish"]
The three tiers of actions, ordered by trust:
- GitHub-managed actions. Repositories under the
actions/organisation:actions/checkout,actions/setup-node,actions/setup-python,actions/cache,actions/upload-artifact. GitHub owns these, patches them, and is the canonical source. Use freely. - First-party actions. Repositories under
github.com/*that the forge or GitHub org maintain:github/codeql-action,github/import-into-pulumi, etc. Owned by GitHub or closely audited. Use with pinning. - Third-party marketplace actions. Anyone with a GitHub account can publish one. Examples: third-party Docker build actions, language installers from community maintainers, integration actions for SaaS APIs. Use with SHA pinning and review.
Pinning by SHA
flowchart LR
A["uses: actions/checkout@v4"] -->|"mutable tag"| B["v4 = whatever the\nmaintainer tagged today"]
C["uses: actions/checkout@\nb4b15b8a7c6d21ea4f18a9f63d6d8e6e3c6e8e3f"] -->|"immutable"| D["Exact commit,\nforever"]
Tags are mutable. The maintainer can move the v4 tag to a
different commit tomorrow; the workflow that said
@v4 yesterday is now running a different action today.
Tags are also forge-controlled — anyone with write access to
the action repository can move them.
Commit SHAs are immutable. A workflow that pins by SHA runs the same code today, next month, and in three years. The trade is that humans do not read SHAs, so the team pairs SHA pinning with a code-review step that approves the specific commit.
# Mutable (do not use in production)
- uses: actions/checkout@v4
# Pinned to a tag's commit (better)
- uses: actions/checkout@v4.1.7
# Pinned to a full SHA (best)
- uses: actions/checkout@b4b15b8a7c6d21ea4f18a9f63d6d8e6e3c6e8e3f
The build-versus-buy decision
flowchart LR
A["Need X in workflow"] --> Q{"Does GitHub\nship an action?"}
Q -->|Yes| USE["Use GitHub-managed\naction, pinned by SHA"]
Q -->|No| Q2{"Does a\nfirst-party action\ncover it?"}
Q2 -->|Yes| USE2["Use first-party\naction, pinned, audited"]
Q2 -->|No| Q3{"Is the third-party\naction trustworthy?"}
Q3 -->|Yes| USE3["Use, pin by SHA,\naudit code"]
Q3 -->|No| WRITE["Write the step inline\nin the workflow"]
The default answer for a step the workflow needs is:
- GitHub-managed action first.
actions/checkout,actions/setup-node,actions/cache— these exist for almost every common need and are the safest choice. - First-party action if GitHub does not ship one. A
github/codeql-actionor similar is owned by the forge. - Third-party action only after audit. Read the code, pin by SHA, document the rationale in the repo.
- Inline shell if no action is trustworthy. A 5-line shell snippet is more auditable than a 200-line action the team has not read.
The marketplace trap
A common anti-pattern:
# Anti-pattern: chained marketplace actions
- uses: org-A/setup-foo@v2
- uses: org-B/setup-bar@v1
- uses: org-C/deploy@v3
Each uses clause is a third-party code dependency. Three
clauses means three supply-chain trusts. The team has to
audit three repositories, monitor three SHA pin updates, and
trust three maintainers. If any one is compromised, the
workflow is compromised.
The counter-pattern: pick a small set of trusted actions,
pin them by SHA, and write the rest inline. A workflow with
two uses: actions/* clauses and ten run: | steps is more
auditable than a workflow with five third-party uses
clauses.
Production discipline
- Pin every action by full-length SHA. Tags are mutable; SHAs are immutable.
- Use GitHub-managed actions by default.
actions/*is the safest set. - Audit any third-party action before use. Read the source, check the maintainers, check the network calls.
- Limit the number of distinct actions. A workflow that uses 15 actions has 15 supply-chain trusts.
- Document the action policy. A
CONTRIBUTING.mdnote explaining “we use GitHub-managed actions pinned by SHA” prevents ad-hoc drift.
Cross-course references
- Linux for Production Sysadmins - Part XXX (PkgTrust) covers the package-pinning patterns that map onto action SHA pinning.
- Ansible for Production Sysadmins - Part XXXVI (ModuleTrust) covers the role-trust patterns that map onto third-party actions.
- Terraform for Production Sysadmins - Part XXXIX (ProvTrust) covers the provider-pinning patterns that match action SHA pinning.
Quiz
Knowledge check · 4 questions
Q1. Why is pinning an action by full-length commit SHA safer than pinning by tag?
Q2. A third-party marketplace action runs with the same access to the runner host and job secrets as a step written inline in the workflow.
Q3. Name the three tiers of actions in order of trust, and which tier GitHub-managed actions like `actions/checkout` belong to.
Q4. Audit a workflow's action list and replace unsafe uses clauses with safer alternatives.
Team T's deploy workflow uses the following actions: `actions/checkout@v4`, `actions/setup-node@v4`, `random-org/fancy-deploy@v2`, `another-org/secret-rotator@main`, `third-org/cleanup@v1`. None are SHA-pinned. The team is concerned about supply-chain risk.
Passing score: 75%. Answers are checked in this browser.