Key numbers and limits
| Number | What it is |
|---|---|
| 100 min · 700 | Exam time · passing score (out of 1,000); retake after 24 hours |
| 2 years | Validity of the certification |
| 6 h · 5 days | Maximum job time on GitHub-hosted · self-hosted runners |
| 360 min | Default timeout-minutes for a job |
| 35 days | Maximum life of a workflow run, waiting and approvals included |
| 24 h | Queue limit for self-hosted jobs; maximum life of GITHUB_TOKEN |
| 256 | Jobs a matrix can generate per run |
| 25 · 65,535 | workflow_dispatch inputs · input payload characters |
| 10 · 50 | Reusable workflow nesting levels · unique reusable workflows per workflow file |
| 3 | Levels of workflow_run chaining; also tags per push that still create an event |
| 5 min · 60 days | Shortest schedule interval · inactivity before public-repo schedules are disabled |
| 10 GB · 7 days | Default cache storage per repository · eviction of caches not accessed |
| 90 days | Default artifact and log retention (public 1–90, private and internal up to 400) |
| 30 days · 50 | Re-run window · re-runs per run |
| 6 · 43,200 min · 30 days | Required reviewers per environment · maximum wait timer · approval wait before the job fails |
| 100 · 1,000 · 100 | Secrets per repository · organisation · environment (48 KB each) |
| 500 · 1,000 · 100 | Variables per repository · organisation · environment (48 KB each, 256 KB per run) |
| 1 MB · 50 MB | One job output · all outputs in a run |
| 1 MiB · 20 | Job summary per step · step summaries shown per job |
| 14 days · 1 day | Offline time before a self-hosted · ephemeral runner is removed |
| 30 days · 1 hour | Runner update window before it stops getting jobs · registration token lifetime |
| 20 · 40 · 60 · 500 | Concurrent standard-runner jobs on Free · Pro · Team · Enterprise (macOS: 5, 5, 5, 50) |
| 1,000/h | REST API requests per repository with GITHUB_TOKEN (15,000 on Enterprise Cloud) |
| 500 KB | Maximum workflow file size |
Renames and retired syntax
The full table is on the Foundations page. The five that cost the most marks:
- Starter workflows = workflow templates.
::set-outputand::save-state→$GITHUB_OUTPUTand$GITHUB_STATE;::set-envand::add-path→$GITHUB_ENVand$GITHUB_PATH.upload-artifact@v3→@v7(anddownload-artifact@v8); v4 and later are immutable per name.runs.using: node16/node20→node24.- Organisation "required workflows" → a ruleset with Require workflows to pass before merging.
Where the study guide, Learn and the docs disagree
When the official sources disagree, these notes follow the live documentation. Here is every discrepancy found while writing them, checked on 5 November 2026, so neither version throws you.
| Source says | The documentation and product say | What to remember |
|---|---|---|
| Study guide: "starter workflows" | "Workflow templates", still stored in actions/starter-workflows | Same feature, two names |
Study guide: YAML anchors, aliases and merge << | Docs describe only & anchors and * aliases (supported since September 2025); community reports say << is still rejected | Expand merged mappings when reading; do not rely on << in a workflow |
| Study guide: "immutable actions rollout on hosted runners" and "immutable actions enforcement" | Immutable actions were removed from GitHub's roadmap on 11 December 2025. What shipped: immutable releases (October 2025), the SHA pinning policy (August 2025) and workflow lockfiles (GA 28 October 2026) | "Make the version unchangeable" = full SHA, lockfile or immutable release |
| Study guide: "Ubuntu 20.04 deprecation, Windows Server 2025 migration for windows-latest" | Both finished in 2025. The live change is ubuntu-latest moving to Ubuntu 26.04 (19 October – 19 November 2026) | The lesson is the same: -latest labels move; pin when you need reproducibility |
| Study guide: "required reviewers for unverified actions" | No single setting does this | Allowed-actions policy (verified creators plus patterns) and CODEOWNERS review of workflow changes |
| Study guide: retention policies "(logs, artifacts, workflow runs)" via REST | One artifact-and-log-retention setting per enterprise, organisation and repository; runs, logs and artifacts can also be deleted individually | Lower levels can only shorten the retention |
| Study guide: "Microsoft associate, expert and specialty certifications expire annually" | The GH-200 certification page: GitHub certifications are valid for two years; GitHub is moving to Microsoft's recertification process and extends certifications expiring before then by six months | Two years for GH-200 |
| Study guide: "Get trained" links only the learning path's part 1 | Part 2 holds the custom actions, GitHub Packages and enterprise modules | Do both parts |
Learn, Create a custom GitHub action: runs.using: "node16" | node24; Node 16 and Node 20 are gone from the runners | Any answer with node16 or node20 is outdated |
Learn samples: actions/checkout@v4, setup-node@v4, actions/cache@v4 | Current majors are checkout@v7, setup-node@v7, cache@v6, upload-artifact@v7, download-artifact@v8 | Old majors still appear in questions; recognise them |
Older material: pull_request_target runs the PR base branch's workflow; checkout of the PR head "just works" | Since 8 December 2025 it always runs the default branch's workflow; checkout@v7 refuses fork PR code there without allow-unsafe-pr-checkout | The safe answer never runs PR code with secrets |
| Older material: reusable workflows nest 4 deep, 20 per file; 10 dispatch inputs | 10 levels and 50 workflows (November 2025); 25 inputs (December 2025) | Use the new numbers |
| Certification page: practice assessment | "Not currently available" | Use the exam sandbox and hands-on practice |
Commonly mistaken topics
Each one reads as a failed run and the run that fixed it.
A job with needs: build runs once build has finished.
It runs only if build succeeded; otherwise it is skipped, and the skip propagates. To run anyway, add if: always() (or !cancelled()) and check needs.build.result.
if: github.ref == 'refs/heads/main' on a notification step runs on main even after a failure.
Without a status function, success() && is added, so it is skipped after a failure. Use if: failure() && … or always() && ….
if: github.event.inputs.dry-run is false when the box is unticked.
github.event.inputs holds strings, and 'false' is truthy. Use inputs.dry-run, which keeps the boolean type.
After echo "X=1" >> "$GITHUB_ENV", $X is set in the same run script and in the next job.
It is set for the following steps of the same job only. Use a shell variable within the step, and a job output to cross jobs.
A pull_request run from a fork can read repository secrets if the workflow references them.
Fork PR runs get no secrets and a read-only GITHUB_TOKEN. pull_request_target has secrets but runs the default branch's code — never check out and run the PR there.
A tag pushed with GITHUB_TOKEN starts the on: push: tags release workflow.
Events created with GITHUB_TOKEN start no new runs, except workflow_dispatch and repository_dispatch. Use a GitHub App token, or dispatch the workflow.
Use actions/cache to hand the build output from the build job to the deploy job.
Use an artifact: it belongs to the run and is guaranteed to be there. Caches are for dependencies, can be evicted at any time, and are scoped by branch.
A composite action can read ${{ secrets.NPM_TOKEN }} directly.
Composite actions have no secrets context. Declare an input and have the workflow pass the secret into it.
The called workflow sees the caller's env values and secrets automatically.
Neither. env is never propagated — use inputs — and secrets must be mapped or passed with secrets: inherit. The github context, by contrast, is the caller's.
A Docker container action works on windows-latest and macos-latest because Docker runs there.
Docker container actions, service containers and job containers need a Linux runner. Write a JavaScript action for cross-platform use.
A job running in container: node:24 reaches its Postgres service at localhost:5432.
In a container job, use the service label as the hostname (postgres:5432). localhost plus a ports mapping is for jobs that run directly on the runner.
Environment secrets are available to every job in the workflow, like repository secrets.
Only a job that names the environment gets them, and only once its protection rules pass — they are read when that job starts.
Adding permissions: pull-requests: write keeps the token's other default scopes.
Once any scope is listed, every unlisted scope becomes none. List everything the job needs — typically contents: read as well.
uses: vendor/action@v1.4.2 always runs the same code.
Tags can be moved or recreated. Only a full commit SHA is fixed — or a commit recorded in a workflow lockfile, or a tag protected by an immutable release.
A schedule runs on every branch that contains the workflow, at the runner's local time.
Schedules run on the latest commit of the default branch, in UTC unless the entry sets a timezone, and may be delayed under load.
Re-running picks up the latest commit and runs with the permissions of whoever clicked re-run.
A re-run uses the same commit and ref, and the privileges of the actor who triggered the original run.
After a failed step with continue-on-error: true, steps.x.conclusion == 'failure'.
outcome is failure; conclusion is success. Test outcome to react to the real result.
OIDC to Azure still needs the client secret stored as a repository secret.
No secret at all: a federated credential trusts GitHub's token. The job needs id-token: write, and the IDs can live in variables.
Delete the workflow file to pause a noisy schedule for a week.
Disable it: the file and history stay, and enabling it again is one click. Deleting is a commit, and other branches with the file can still trigger it.
Decision trees
flowchart TD
Q["Share automation across repositories"] --> A{"Should later changes<br/>reach every user?"}
A -->|"no — a starting point"| T["Workflow template"]
A -->|"yes"| B{"Whole jobs, with their own<br/>runners, environments, secrets?"}
B -->|"yes"| R["Reusable workflow<br/>on: workflow_call"]
B -->|"no — steps inside a job"| C["Composite action"] flowchart TD
Q["The workflow must authenticate to…"] --> A{"This repository on GitHub?"}
A -->|"yes"| GT["GITHUB_TOKEN<br/>with minimal permissions"]
A -->|"no"| B{"A cloud provider?"}
B -->|"yes"| OIDC["OIDC federation<br/>id-token: write, no stored key"]
B -->|"no"| C{"Other repositories, or must<br/>trigger other workflows?"}
C -->|"yes"| APP["GitHub App installation token"]
C -->|"no — a third-party service"| SEC["Environment secret<br/>scoped to the deploying job"] Scenario → answer lookup
| The scenario says… | Reach for |
|---|---|
"Run only when files under docs/ change" | on: push: paths: ['docs/**'] |
| "Let operators start a deploy and pick the target" | workflow_dispatch with a choice or environment input |
| "An external system should start the workflow" | repository_dispatch with an event_type |
| "Run the same tests on three OSes and two runtimes" | strategy.matrix; fail-fast: false to see every result |
| "Tests need a real database" | services: with a health check |
"Avoid repeating the same env block in one file" | A YAML anchor and alias |
| "Pass a version from one job to the next" | $GITHUB_OUTPUT → job outputs → needs.<job>.outputs |
| "Show test results on the run page" | $GITHUB_STEP_SUMMARY |
| "A new push should cancel the PR's running build" | concurrency with cancel-in-progress: true |
| "Deployments must run one at a time, none dropped" | concurrency with queue: max |
| "Production needs a human approval" | Environment with required reviewers |
| "Why was this step skipped?" | Re-run with debug logging; read the ##[debug] condition trace |
| "Retry only the flaky Windows leg" | Re-run that matrix job |
| "Every team's pipeline must follow one central definition" | Reusable workflow, plus a ruleset requiring it |
| "Bundle three setup steps used in many jobs" | Composite action |
| "Share a private action with the rest of the organisation" | The action repository's Actions Access setting |
| "Only allow GitHub and verified-creator actions" | Allowed-actions policy at organisation or enterprise level |
| "Stop one compromised action everywhere, now" | A !owner/repo@* blocklist pattern |
| "Jobs fail behind the organisation's IP allow list" | Larger runners with static IPs, or self-hosted runners |
| "Keep production runners for the deploy workflow only" | Runner group restricted to selected workflows |
| "Clean machine for every job on our own infrastructure" | Ephemeral self-hosted runners, autoscaled with ARC |
| "Which Python versions are preinstalled?" | The image README in actions/runner-images, linked from Set up job |
| "Rotate a secret from a script" | Fetch the public key, encrypt, PUT …/actions/secrets/{name} — or gh secret set |
"Issue titles end up in a run: script" | Pass them through env: and quote them |
| "Deploy to the cloud without long-lived keys" | OIDC: id-token: write and a federated credential |
| "Prove a binary came from our pipeline" | actions/attest, then gh attestation verify before deploy |
| "Make sure the action code never changes under us" | Full-SHA pins (enforced by policy) or a workflow lockfile |
| "Artifacts are filling the storage quota" | Shorter retention-days and a lower retention policy via the API |
Exam-day caveats
Read the YAML before the question
Many items are a workflow followed by "what happens?". Read on:, permissions and every
needs and if before you read the answer options — the trap is usually a filter that does
not match, a skipped dependency or a missing scope.
Watch for the old syntax in the options
::set-output, @v3 artifact actions, node16 and
ubuntu-20.04 make an option wrong today, however tidy it looks.
Prefer the enforceable answer
When two options both "work", the exam tends to prefer the one that is enforced by the platform — a policy, a protection rule, a permission, a pin — over one that relies on people remembering to do the right thing.
Pre-exam checklist
- I can name the event, filters and branch behind any
on:block, includingschedule,workflow_dispatchandpull_request_target. - I can expand a matrix with
includeandexclude, and an anchored file, in my head. - I know which contexts are available in a job
if,runs-onand a stepif. - I can move a value step → step, job → job and reusable workflow → caller.
- I can tell a cache from an artifact, and know their scope, retention and limits.
- I can write
action.ymlfor JavaScript, Docker and composite actions, and know where each runs. - I know the Marketplace publishing requirements and why consumers pin to SHAs.
- I can set allowed-actions, SHA pinning, fork PR and token-permission policies, and know they only tighten downwards.
- I can register a self-hosted runner, put it in a group and target it with labels.
- I know secret and variable scopes, precedence and limits, and how the API encrypts secrets.
- I can spot script injection and fix it with
env:, and explain whypull_request_targetis dangerous. - I can configure OIDC to a cloud provider and read a subject claim.
- I can attest an artifact and verify it before deployment.
- I have tried the exam sandbox.