GitHub Outage CI Fallback: Mirrors, Runners, and a Break-Glass Deploy Path
On August 17, 2026, a large share of the software industry discovered that its deploy button was a single point of failure. GitHub’s platform degraded at roughly 13:40 UTC and was not marked resolved until 21:15 UTC, close to eight hours later. Actions, the API, pull requests, issues, Pages, Git operations, webhooks and Copilot were all affected, and teams that could not merge, build or ship a hotfix sat and refreshed a status page. A github outage ci fallback is the engineering answer: a rehearsed, boring second path from commit to production that does not depend on the first platform being healthy.
This matters now because the concentration risk keeps growing. Source hosting, review, CI, package storage, identity federation and AI assistants increasingly live behind one vendor’s control plane, so one capacity failure removes all of them at once. In this tutorial you will map the blast radius of a typical pipeline, build a continuous Git mirror, write a fallback CI path and a break-glass deploy script you can actually run, and finish with a runbook.
What this covers: what failed on August 17 (with sources and caveats), a dependency map of a GitHub-centred pipeline, mirroring to a second host, a fallback runner design, OIDC failure modes, dependency vendoring, runnable scripts, the trade-offs, and a checklist.
Context and Background
Start with what is publicly known, and be honest about how much of it is secondhand. Contemporary reporting (for example CyberInsider’s write-up) puts the start of the incident at 13:40 UTC on August 17, 2026, with recovery reported from about 16:36 UTC and a resolved status at about 21:15 UTC. Affected surfaces included the API, Actions, pull requests, issues, Pages, Git operations, webhooks and Copilot. Authentication paths were hit too: SAML and OIDC sign-in, SCIM provisioning and Team Sync were reported as impacted, which locked some enterprise users out of their development environments. At the peak, reporting cited roughly 20 percent errors on web and API traffic and roughly 50 percent errors on archive and raw-content downloads.
The root cause was not disclosed in the first wave of coverage. GitHub subsequently published a post, “The August 17 outage and the work ahead,” on the GitHub blog. We could not retrieve that post directly in this run; the details below come from a secondary summary of it on daily.dev, so treat them as reported, not independently verified. According to that summary, a critical infrastructure component in a Central US data centre failed to scale when traffic reached a new peak. The resulting capacity pressure produced authentication failures across several services, and a client-side retry loop in Copilot services added traffic during recovery and slowed restoration. The summary also says neither this incident nor an earlier August 6 Actions failure was caused by a code change; both were framed as capacity failures. Read GitHub’s own post and the GitHub status history before quoting any of this in your own postmortem.
Two lessons from that framing matter for pipeline design. First, a capacity failure is correlated: when a shared component saturates, many nominally separate features degrade together, so “Actions is down but Git is fine” is not a safe assumption. Second, retries amplify. A client that retries aggressively during a partial outage keeps the platform saturated and keeps your own jobs failing. We will come back to retry budgets in your own tooling.
This is not an argument against GitHub. It is an argument that any single platform, however good, has a tail of multi-hour failures, and that your recovery objectives should be set by your business rather than inherited from a vendor’s availability. If your service-level objective for shipping a security fix is “under two hours,” a vendor outage of eight hours is simply outside that objective unless you have a second path. For how to reason about severity and on-call response, see our guide to incident management architecture, severity levels and error budgets. For the hardening of the primary path itself, see GitHub Actions at scale with OIDC federation and runner controllers.
A final piece of context: most teams already have partial resilience without knowing it. Developers hold full local clones, so the code itself is rarely lost. What is lost during an outage is coordination: the review record, the trigger mechanism, the build environment, the credentials broker and the audit trail. A fallback design is therefore less about copying code and more about reproducing those five functions somewhere else, deliberately and with controls.
Mapping the Blast Radius of a GitHub-Centred Pipeline
Direct answer: to find your blast radius, list every step between “commit exists” and “artifact is running in production,” and mark which ones call a GitHub-hosted service. Each marked step is a failure point. Typical pipelines have six to ten of them, and several are invisible until they fail, such as a third-party action downloaded at job start or an OIDC token requested for cloud credentials.

Figure 1: Dependency map of a typical GitHub-centred pipeline. Every box that touches GitHub is a candidate failure point during an outage.
The diagram shows the dependency chain for an ordinary containerised service. A push triggers a webhook, a runner is scheduled, the job requests an OpenID Connect (OIDC) token, exchanges it for cloud credentials, pulls dependencies, builds, stores artifacts and deploys. The pull request path adds a required-checks merge gate that feeds back into the same machinery. Read the figure as a graph of “what must be alive for a change to ship,” not as a data-flow diagram.
Inventory the hidden dependencies
Start the inventory from the workflow files, not from memory. For each workflow, list: the trigger (push, pull request, schedule, workflow_dispatch), the runner type (GitHub-hosted or self-hosted), every uses: reference (each is a fetch from GitHub at job start unless pinned and cached), the credential mechanism (OIDC or stored secrets), the artifact and cache storage, the package registries touched, and the deploy mechanism. A short script that greps uses: lines across your repositories is a worthwhile first hour; the output often surprises people, because reusable workflows and composite actions nest several levels deep.
Then classify each dependency by what happens when it is unreachable:
- Hard blocking: the job cannot start. Examples: webhook delivery, runner assignment, the Actions API for
workflow_dispatch, the merge gate. - Soft blocking: the job starts but fails mid-run. Examples: action download, OIDC token request, cache restore, artifact upload, container registry pulls.
- Degrading: the job succeeds with reduced function. Examples: status checks that do not post, notifications that do not arrive, Copilot review.
During the August 17 incident, reporting indicated that webhooks, Actions and authentication paths were all affected. That is a reminder that hard-blocking and soft-blocking categories can fail in the same hour. A fallback that depends on any GitHub-hosted component for its own trigger is not a fallback.
Separate the control plane from the data plane
The most useful design distinction is between the control plane (who may ship, what is approved, what triggers a run) and the data plane (the bytes: source, dependencies, build output). Git is a distributed system, so the data plane for source is already replicated onto every developer laptop and every runner checkout. The control plane is the part that lives only on GitHub: branch protection, required reviews, the merge queue, secrets, environment approvals. A resilient design either replicates the control plane’s decisions into a second place or substitutes a stricter manual procedure while the first is down.
Quantify what an hour of outage costs you
Put numbers on it before you propose spend. Take your count of production deploys per week, the share that are urgent (security fixes, rollback of a bad release, customer-impacting bug), and your recovery objective for those. An eight-hour outage that lands in a business day blocks every urgent change in that window. If you ship ten urgent changes a quarter and one outage window per year overlaps with one of them, a fallback path that costs a few engineer-days to build and a few minutes per month to rehearse is cheap insurance. If you ship only weekly and nothing is urgent, the right fallback may be nothing more than a documented “wait for recovery” policy. The tutorial below scales both ways; build the layers you need.
Reference Architecture: Mirror, Fallback CI, Break-Glass Deploy
Direct answer: a workable fallback has three parts. A git mirror keeps a second Git host within minutes of the primary. A fallback CI runner on that second host (or a plain VM) can build and test from the mirror using vendored dependencies. A break-glass deploy path ships a reviewed commit using credentials that do not depend on GitHub’s identity services. Everything else is optimisation.

Figure 2: Reference fallback architecture. A scheduled mirror job feeds a second Git host; a fallback runner builds from vendored dependencies and deploys to the same target.
The architecture deliberately duplicates only what an emergency needs. It does not try to run your full pull request workflow on a second platform, which would double your maintenance burden for something you use twice a year. It aims for one capability: take a specific approved commit and produce a deployable artifact without GitHub being reachable.
Layer 1: A continuous git mirror
The mirror is the foundation, and it is the cheapest layer. Pick a second host that does not share infrastructure with the first: a self-hosted Gitea or Forgejo instance, GitLab (SaaS or self-managed), a cloud provider’s repository service, or even a bare repository over SSH on a hardened VM. Independence matters more than features; a second host that sits behind the same cloud region or identity provider as your primary shares failure modes you were trying to escape.
Git offers two relevant push modes. git push --mirror pushes all refs under refs/ and deletes remote refs that no longer exist locally, producing an exact replica. That is what you want for a read-only mirror. git push --all plus --tags is gentler and does not delete refs, which is safer if the second host is also a working location. Use --mirror for a one-way replica that no human pushes to, and protect it so that is true.
You can drive the sync from three places, each with a failure profile:
- A job on the primary (for example, an Actions workflow on push). Easy and near real time, but it dies exactly when you need it, so use it only as an accelerator.
- A scheduled job outside GitHub (cron, a systemd timer, a small container on a separate cloud). Pulls with
git remote updateand pushes onward. This survives a GitHub outage in the sense that it keeps trying; it simply has nothing new to fetch while GitHub is down. Run it every one to five minutes. - A platform pull-mirror feature on the second host (Gitea, Forgejo and GitLab each offer one). It is managed for you, but check how it authenticates and how often it polls.
Run at least the scheduled external job. The worst-case staleness of your mirror, your recovery point objective for source, is then the interval of that job plus the time of the last successful fetch before the outage started. Measure it and alert on it, because a mirror that silently stopped syncing three weeks ago is worse than none, as it creates false confidence.
Layer 2: A fallback CI path that does not call home
The fallback runner needs to execute your build and tests from the mirror. There are two honest options. The first is to run the second host’s own CI (GitLab CI, Forgejo Actions, Woodpecker, or a cloud build service) with a pipeline definition that mirrors your essential steps. The second is to skip a CI platform entirely and use a self-hosted runner machine that executes a single shell or Dagger entry point. The second is easier to keep in sync, because the workflow YAML becomes a thin wrapper around a script that you can run anywhere.
That is the core design principle: put the logic in a script, not in YAML. If your GitHub workflow is run: ./ci/build.sh and run: ./ci/test.sh, the fallback path runs the same two commands from the same repository. Programmable pipeline tools make this natural; our comparison of Dagger’s container-native pipelines against GitHub Actions explains why a pipeline defined in code can run identically on a laptop, a self-hosted runner or any CI. A pipeline whose logic lives in marketplace actions and expression syntax, in contrast, has to be ported by hand under pressure.
Self-hosted runners deserve a caveat. A GitHub self-hosted runner still registers with and polls GitHub to receive jobs, so it does not help when the Actions service is down. A self-hosted runner is a fallback only when it also serves a second platform, or when you run the script on it directly via SSH or a local scheduler. This is the single most common misconception in “we have self-hosted runners, so we are fine” conversations.
Layer 3: A break-glass deploy path
The last mile is deployment. In most organisations production credentials are obtained inside a GitHub workflow through OIDC federation, so the very mechanism that gives CI its short-lived credentials is also dependent on GitHub being up (more on this below). The break-glass path replaces that with a narrowly scoped, normally dormant credential held somewhere you control, used from the fallback runner or an operator’s workstation, with every use logged and reviewed afterwards.
Three controls keep this safe. It requires two people (a requester and an approver) or an equivalent strong-authentication step. It deploys only commits that exist in the mirror and whose hashes match what the primary had approved before the outage, so the approval record stays meaningful. And the credential is scoped to deploying, not to administering: it can push an image tag or run a deployment job, not create IAM users or read databases.
Pre-position what the fallback needs
A fallback that must download things at the moment of need fails in the same way the primary did. Pre-position four items: a pinned base builder image copied to a registry you control; vendored or proxied language dependencies (for example a Go module proxy or a private npm, PyPI or Maven mirror); copies of any third-party action code you use, vendored into your own repository at a pinned commit; and a copy of the deploy tooling (kubectl, helm, terraform, cloud CLIs) at pinned versions. Treat the fallback environment as an offline-capable build host and you will rarely be surprised.
Deeper Analysis: Runnable Scripts, OIDC Failure Modes, and Drills
This section is the hands-on part. Everything below is plain POSIX shell and standard Git behaviour; adapt names and URLs to your environment. Nothing here depends on a vendor feature we could not verify, but test every script in a non-production repository before trusting it.
The mirror sync script
This script keeps a bare mirror clone on a utility host and pushes it to the second host. It uses git remote update --prune, which fetches all refs and prunes deleted ones, and then git push --mirror to the secondary. It writes a heartbeat file so monitoring can alert on staleness.
#!/usr/bin/env bash
# mirror-sync.sh - one-way replica of PRIMARY into SECONDARY
set -euo pipefail
PRIMARY="${PRIMARY:?set PRIMARY, e.g. git@github.com:acme/payments.git}"
SECONDARY="${SECONDARY:?set SECONDARY, e.g. git@git.backup.example:acme/payments.git}"
WORKDIR="${WORKDIR:-/var/lib/git-mirror}"
NAME="$(basename "${PRIMARY%.git}").git"
HEARTBEAT="${HEARTBEAT:-/var/lib/git-mirror/${NAME}.heartbeat}"
mkdir -p "$WORKDIR"
cd "$WORKDIR"
if [ ! -d "$NAME" ]; then
git clone --mirror "$PRIMARY" "$NAME"
fi
cd "$NAME"
git remote set-url origin "$PRIMARY"
# Fetch with a hard timeout so a degraded primary cannot hang the job.
if timeout 120 git remote update --prune; then
git push --mirror "$SECONDARY"
date -u +%s > "$HEARTBEAT"
echo "synced $NAME at $(date -u +%FT%TZ)"
else
echo "fetch from primary failed or timed out; mirror left as-is" >&2
exit 0 # not an error: during an outage we expect this
fi
Schedule it with a systemd timer or cron every few minutes on a host that is not inside the same blast radius as GitHub. Two details are worth explaining. The timeout wrapper prevents a half-working primary from stalling the job indefinitely, which was the shape of many failures in partial outages. And a failed fetch exits zero deliberately: during an outage the sync is expected to fail, and you want alerting driven by the age of the heartbeat, not by noisy job failures.
Add a monitor that fires when the heartbeat is older than a threshold, for example 15 minutes, and routes to a channel that does not depend on GitHub. A second check should compare git ls-remote output for your protected branches on both hosts when the primary is healthy, so you detect divergence rather than only staleness.
The fallback CI entry point
The fallback runner executes the same script your GitHub workflow calls. Keep a tiny wrapper that checks out a specific commit from the mirror, verifies it, builds in a container with pinned images, and emits an artifact and a checksum.
#!/usr/bin/env bash
# fallback-ci.sh - build a specific commit from the mirror, offline-friendly
set -euo pipefail
MIRROR="${MIRROR:?set MIRROR url}"
COMMIT="${1:?usage: fallback-ci.sh <full-commit-sha>}"
BUILDER_IMAGE="${BUILDER_IMAGE:?pinned builder, e.g. registry.internal/ci/builder@sha256:...}"
OUT="${OUT:-/var/tmp/fallback-out}"
rm -rf "$OUT" && mkdir -p "$OUT/src"
git clone --no-checkout "$MIRROR" "$OUT/src"
cd "$OUT/src"
git checkout --detach "$COMMIT"
# Refuse anything that is not a full 40 or 64 hex commit id.
echo "$COMMIT" | grep -Eq '^[0-9a-f]{40}([0-9a-f]{24})?$' || { echo "bad sha" >&2; exit 2; }
docker run --rm \
-v "$OUT/src:/work" -w /work \
--network "${BUILD_NETWORK:-bridge}" \
"$BUILDER_IMAGE" \
sh -c './ci/build.sh && ./ci/test.sh'
tar -C "$OUT/src" -czf "$OUT/artifact.tgz" dist/
sha256sum "$OUT/artifact.tgz" | tee "$OUT/artifact.sha256"
git rev-parse HEAD > "$OUT/built-commit.txt"
Pinning the builder by digest rather than tag matters: a tag can be moved, a digest cannot. The script assumes your repository already exposes ci/build.sh and ci/test.sh; if it does not, creating those two files is the highest-value refactor in this entire article. You can also set BUILD_NETWORK=none for builds that are fully vendored, which proves the build does not secretly depend on the network.
The break-glass deploy script
The deploy step needs a human gate and an audit trail. This example assumes a container-based service deployed by updating an image tag; swap the final command for your own mechanism. The approval check here is a simple second-person token file for illustration; in a real environment use your PAM, a hardware key, or a ticketing-system check.
#!/usr/bin/env bash
# break-glass-deploy.sh - emergency deploy of an already built artifact
set -euo pipefail
ARTIFACT_DIR="${1:?usage: break-glass-deploy.sh <out-dir> <ticket-id>}"
TICKET="${2:?ticket id required}"
LOG="${LOG:-/var/log/break-glass.log}"
APPROVAL_FILE="${APPROVAL_FILE:-/run/break-glass/approval}"
SHA="$(cat "$ARTIFACT_DIR/built-commit.txt")"
WHO="$(id -un)"
# 1. Second-person approval must name this exact commit and ticket.
grep -qx "$TICKET $SHA" "$APPROVAL_FILE" || { echo "no approval for $TICKET $SHA" >&2; exit 3; }
# 2. Verify artifact integrity.
( cd "$ARTIFACT_DIR" && sha256sum -c artifact.sha256 )
# 3. Record intent before acting.
printf '%s START user=%s ticket=%s sha=%s\n' "$(date -u +%FT%TZ)" "$WHO" "$TICKET" "$SHA" >> "$LOG"
# 4. Deploy (replace with your mechanism).
docker load -i "$ARTIFACT_DIR/image.tar" 2>/dev/null || true
kubectl --context prod-breakglass set image deploy/payments app="registry.internal/payments:${SHA}"
kubectl --context prod-breakglass rollout status deploy/payments --timeout=300s
printf '%s DONE user=%s ticket=%s sha=%s\n' "$(date -u +%FT%TZ)" "$WHO" "$TICKET" "$SHA" >> "$LOG"
The prod-breakglass context is the point: it is a distinct kubeconfig context bound to a distinct, normally disabled credential with deploy-only rights. Do not reuse the same service account CI normally uses through OIDC; the whole premise is that those paths are unavailable.
OIDC failure modes in the primary pipeline
The August 17 reporting noted that SAML and OIDC authentication were among the impacted functions, so it is worth spelling out how OIDC federation fails in a CI context. In the standard setup, a workflow job requests a signed JSON Web Token from GitHub’s token service, presents it to your cloud’s security token service, and receives short-lived credentials if the token’s claims (repository, branch, environment) match a trust policy. That chain has four dependencies: the job must start, the token endpoint must answer, the cloud must be able to validate the token against GitHub’s published signing keys, and the trust policy must match the claims.

Figure 3: OIDC failure and fallback. The runner cannot obtain an ID token, backs off within a budget, and then switches to a break-glass credential.
Failure modes to plan for:
- Token endpoint unavailable or slow. The job fails at the credential step even though the build succeeded. The fix is to bound retries (a total time budget, not an infinite loop) and fail fast into the break-glass path.
- Key discovery failure. Your cloud’s identity provider fetches GitHub’s public keys to verify tokens. Providers cache keys, but check how long, and what happens on cache expiry during an outage.
- Partial auth degradation. Reporting said sporadic authentication failures continued after the main recovery, and that GitHub partially disabled authentication token retries as a mitigation. Expect intermittent success; do not conclude “it works now” from one green run.
- Claim mismatch under the fallback. A build from the second host presents no GitHub OIDC claims at all. Your cloud trust policy needs a separate, stricter role for the break-glass identity rather than loosening the existing one.
Retry budgets for your own tooling
Because the reported contributing factor included a client-side retry loop that raised load during recovery, apply the same discipline to your scripts. Use exponential backoff with jitter, a maximum attempt count, and a total deadline. In shell, a small helper is enough:
retry() { # retry <max-seconds> <cmd...>
local deadline=$(( $(date +%s) + $1 )); shift
local delay=2
until "$@"; do
[ "$(date +%s)" -ge "$deadline" ] && return 1
sleep $(( delay + RANDOM % delay ))
delay=$(( delay < 60 ? delay * 2 : 60 ))
done
}
# example: retry 300 curl -fsS https://api.example.invalid/health
A retry helper with a deadline prevents your pipeline from becoming part of the problem, and prevents a fifteen-minute step from becoming a three-hour one.
Vendoring dependencies and caching artifacts
Even a perfect mirror cannot build if the build downloads from the internet. Walk through the dependency classes your build touches and give each a local source of truth.
Language packages. Run a caching proxy or private registry in front of public ones: Go has the module proxy protocol and a GOFLAGS=-mod=vendor option, Node, Python and Java ecosystems have mature registry proxies, and Rust supports cargo vendor. The pattern is identical everywhere: your CI pulls from a registry you operate, which pulls through to the public one when healthy and serves cached content when not. Verify that lockfiles are committed so a fallback build resolves the same versions as the primary build; an unlocked build during an emergency is a second incident waiting to happen.
Container base images. Mirror the images you build from into your own registry and reference them by digest. Registry rate limits and outages are an independent risk category, and a fallback that pulls a public base image at 3 a.m. during an unrelated registry hiccup will fail twice.
Third-party actions. Every uses: owner/action@ref is code fetched from GitHub at job start. For the primary pipeline, pin to a full commit SHA, which is a supply-chain best practice irrespective of outages. For the fallback, extract what the action actually does into a script in your repository. Many popular actions are thin wrappers over a CLI; the fallback can call the CLI directly.
Build caches and artifacts. Actions caches and artifact storage are GitHub services. If your build is slow without a cache, the fallback will be slow too; decide whether that is acceptable, or maintain an independent cache (an S3-compatible bucket or a remote build cache such as the one offered by your build tool) that both paths use. Using one shared, external cache has a side benefit: the two pipelines stay behaviourally similar.
Release artifacts and downloads. Reporting for August 17 cited roughly 50 percent errors on archive and raw-content downloads. If any of your tooling downloads release tarballs or raw files from GitHub (installers, curl | sh bootstraps, go install from a GitHub path, Helm charts hosted on GitHub Pages), those steps are exposed. Mirror the specific artifacts you use into object storage you control and verify them by checksum.
Handling the trigger problem
Hard-blocking dependencies cluster around the trigger. Webhooks that fail to deliver mean your CI never starts; the Actions API being degraded means workflow_dispatch calls from your chatops bot fail. The fallback must be triggered by something independent: a human running fallback-ci.sh over SSH, a chat command routed to a service on your own infrastructure, or a poller on the fallback host that watches the mirror for new commits on a designated emergency branch. Prefer the human or chat trigger. An automatic trigger that builds every commit during an outage can race with the primary path when it recovers, producing duplicate deploys.
Reconciliation after recovery
The hardest part of a fallback is coming back. During the outage the mirror, not the primary, may have received an emergency commit. Decide in advance how those commits flow back: the usual answer is that an emergency fix is committed to the mirror’s emergency/* branch, deployed, and then pushed to the primary as a normal pull request once it recovers, where it receives the review it skipped. Never push to the mirror’s mainline branches directly while the mirror is configured with --mirror from the primary, because the next sync will either overwrite or reject your change. Dedicating a namespace for emergency refs and excluding it from the mirror’s prune step avoids that collision. In practice that means syncing explicit refspecs for the branches you mirror instead of --mirror, if you intend the second host to ever accept writes.
Drills: prove it before you need it
Run a game-day twice a year. Block outbound traffic from a test runner to github.com using a firewall rule or /etc/hosts entry pointing at an unroutable address, and then try to ship a trivial change using only the runbook. Time every step. The first drill almost always finds a missing credential, an unpinned image, a documentation gap or a person who has never seen the script. Record the elapsed time from “declare” to “deployed”; that measured number is your actual recovery time objective for urgent changes, and it is the number worth reporting to management.
Trade-offs, Gotchas, and What Goes Wrong
A fallback path is itself a production system, with its own risks, and it should be introduced with eyes open.

Figure 4: Runbook decision flow. Deploys through the fallback happen only for urgent changes with a fresh mirror, and always end in reconciliation.
The decision flow encodes the key judgement: most of the time, the right response to a platform outage is to wait. Break-glass deploys carry real risk (skipped review, unfamiliar tooling, tired operators), so the flow requires three yeses before acting: the outage is real, the change is urgent, and the mirror is fresh. Anything else queues. A tempting anti-pattern is to treat the fallback as a general-purpose alternate path for everyday convenience; that erodes controls and turns the emergency channel into the default one.
Security drift is the largest risk. The primary pipeline gets branch protection, required reviews, secret scanning, signed artifacts and policy checks. A hurried second path usually has none of them. Mitigate by restricting what the fallback can ship (only commits already approved on the primary, verified by hash), by scoping the credential tightly, and by auditing every use. If your organisation signs artifacts, the fallback must sign them too, with a key that is protected at least as well.
Mirror staleness and divergence. A mirror that is two hours behind may lack the very fix you need. Show the heartbeat age in the runbook’s first step, and design for the case where the fix exists only on a developer’s laptop. Git makes it possible to push from a laptop to the second host directly; the runbook should say who may do this and how the approval is recorded.
Second-host correlation. If your secondary host runs in the same cloud region, uses the same DNS provider or authenticates through the same identity provider as your primary, then some failure types hit both. The August 17 account describes capacity pressure leading to authentication failures. Check what your secondary depends on for authentication, and keep at least one login path that does not rely on a vendor single sign-on during an emergency.
Secrets duplication. The fallback needs secrets that GitHub normally holds as Actions secrets. Copying them to a second system doubles the places they can leak. Prefer short-lived credentials issued from your own broker, and keep the dormant break-glass credential in a vault with access logging and alerting on any read.
Cost and attention. The standing cost is small (a small VM, a registry, a timer), but the attention cost is real: someone must patch the runner, rotate the credential and run the drill. If nobody owns it, it rots, and a rotted fallback is worse than none because the team believes it works. Assign an explicit owner and put the drill on the calendar.
Compliance. Some regimes require that all production changes pass a defined review. A break-glass deploy is a documented exception in most frameworks, but only if the exception process exists, is approved and leaves records. Involve your compliance owner before the first drill, not after the first real use.
What a fallback cannot fix. It does not restore code review, the pull request history, issue tracking, Copilot or Pages. It also does not help if the outage is on a dependency outside GitHub. Be explicit about scope in the runbook so nobody expects more from it than it provides.
Practical Recommendations
Resist the urge to build everything at once. A staged rollout gives value early and keeps maintenance manageable. In the first week, create the dependency inventory and move build logic into scripts, because both are valuable even if you never build another thing. In the second, stand up the mirror with the heartbeat monitor. In the third, build the fallback runner and prove it can build from the mirror with the network to GitHub blocked. Only then add the break-glass credential and the deploy script, with approvals and logging, and finish by writing and rehearsing the runbook.
Match the investment to your risk. A team with weekly releases and no urgent-change requirement needs the mirror and the documented wait policy. A team with a customer-facing service and a security response obligation needs the full path. A regulated team needs the full path plus the compliance sign-off and tested audit evidence. Do not copy another organisation’s depth.
Finally, make the outage part of your operating rhythm. Review the vendor’s postmortem when it is published, compare the failure mode to your dependency map, and update both. The reported themes from GitHub’s account (retry budgets, isolating critical systems, removing shared dependencies) are the same themes your own platform team should apply to your pipeline.
Checklist:
- [ ] Inventory every
uses:, token request, registry and download in your workflows. - [ ] Move build, test and package logic into repository scripts (
ci/build.sh,ci/test.sh). - [ ] Run a scheduled git mirror on infrastructure independent of GitHub; alert on heartbeat age.
- [ ] Pin builder images by digest and mirror them to your own registry.
- [ ] Vendor or proxy language dependencies and third-party action code.
- [ ] Build a fallback runner that does not poll GitHub; prove it with GitHub blocked.
- [ ] Create a deploy-only, normally dormant break-glass credential with two-person approval.
- [ ] Bound all retries with backoff, jitter and a total deadline.
- [ ] Write the runbook, name an owner and drill twice a year.
- [ ] Define how emergency commits flow back to the primary for review.
Frequently Asked Questions
What happened in the GitHub outage on August 17, 2026?
Reporting says GitHub degraded around 13:40 UTC and was marked resolved around 21:15 UTC, nearly eight hours later. Actions, the API, pull requests, issues, Pages, Git operations, webhooks, Copilot and authentication paths such as SAML and OIDC were affected. A secondary summary of GitHub’s own post attributes it to a critical component in a Central US data centre failing to scale at a new traffic peak, amplified by a Copilot client retry loop. Verify against GitHub’s post.
Do self-hosted runners keep working when GitHub Actions is down?
Generally no. A GitHub self-hosted runner registers with GitHub and polls it for jobs, so if the Actions service is degraded the runner receives nothing. Self-hosted runners help with capacity and network control, not with platform independence. For a real fallback, run the same build script on that machine through SSH, a local scheduler or a second platform’s CI, so the job trigger no longer depends on GitHub.
How often should a git mirror sync?
Every one to five minutes is typical and inexpensive for most repositories, since Git only transfers new objects. What matters more is monitoring: write a heartbeat timestamp after each successful sync and alert when it is older than a threshold such as 15 minutes. That age is your source recovery point, and you should know it before an incident rather than discover it during one.
Can I use Dagger or another portable pipeline tool as the fallback?
Yes, and it is a good fit. Defining pipeline logic as code that runs in containers means the same entry point executes on a laptop, a self-hosted machine or any CI platform. You still need the fallback to pull pinned images and dependencies from sources you control, and you still need a trigger and credentials that do not depend on GitHub being available.
Is a break-glass deploy path a security risk?
It can be, if unmanaged. The path bypasses normal review gates, so control it: deploy only commits already approved on the primary and verified by hash, require two-person approval, scope the credential to deployment only, keep it dormant in a vault with alerting, and log every use. Treat each use as an incident that gets a retrospective and a reconciliation pull request once the primary recovers.
Should I move off GitHub after an outage like this?
Usually not. Every platform has a tail of long outages, and migrating trades one set of risks for another. The more effective response is reducing coupling: scripted pipelines, a mirror, vendored dependencies and a rehearsed emergency path. Reassess if the vendor’s reliability trend, reported incident count or postmortems consistently conflict with your recovery objectives, and base that decision on your own measured downtime cost.
Further Reading
- GitHub Actions at scale: OIDC federation, reusable workflows and runner controllers for hardening the primary path.
- Dagger and programmable CI/CD versus GitHub Actions for pipelines as portable code.
- Incident management architecture: severity levels, on-call, postmortems and error budgets for running the response.
- GitHub Status history and GitHub’s post “The August 17 outage and the work ahead” on the GitHub blog (read it directly; this article relied on secondary summaries of it).
- CyberInsider coverage of the eight-hour outage for the reported timeline.
By Riju — about
