DEV Community

Cover image for GitHub Actions Outage: Navigating the 9-Hour Storm
Dave Kurian
Dave Kurian

Posted on • Originally published at otf-kit.dev

GitHub Actions Outage: Navigating the 9-Hour Storm

On August 6, 2026, at 15:22 UTC, GitHub Actions started reporting degraded performance. Twenty minutes later, workflow runs were failing to start, REST API calls were returning errors, and developers were hitting rate limits that made no sense at the time. The outage lasted about nine hours. It didn't stay contained to CI/CD.

What actually broke

The headline was "GitHub Actions down for nine hours." The reality was wider. The same scheduling failure that broke Actions cascaded into Copilot's code review and coding agent, took GitHub Pages offline, and backed up webhook delivery across the platform. According to GitHub's engineers, the root cause was "invalid job assignments in the scheduling layer" — a single bottleneck failing and taking several product lines with it.

GitHub's mitigation statement, hours into the disruption: "engineers have applied a number of mitigations and are rolling out a further fix." By the time the fix rolled out, Enterprise Importer migration jobs were still suspended — The Register's coverage of the incident confirms this — and Copilot's degraded features hadn't fully recovered.

For a developer with a release scheduled that afternoon, the practical summary: CI broken, code review broken, Pages broken, webhooks not firing. One outage, four product surfaces.

The self-hosted runner myth

The most common assumption developers reach for: "I'll just run my own runners." This is the escape hatch. It isn't.

Self-hosted runners still depend on GitHub's scheduling service to receive and assign jobs. When that scheduling layer breaks, your self-hosted runner doesn't quietly keep working — it registers errors or hits rate limiting, the same as hosted runners. Hacker News threads from the day of the outage made the point bluntly: "Even self hosted workers don't work during these outages."

This is the part that's easy to miss when you're reading GitHub's docs on self-hosted runners. The worker process lives on your infrastructure. The scheduling layer that tells it what to run, when, and with what secrets does not. You've outsourced the most failure-prone part of CI to a service you don't control, then paid the electricity bill to run your half.

How the scheduling layer actually works

Every GitHub Actions run — hosted or self-hosted — moves through GitHub's central scheduling service. That service is responsible for:

  1. Reading the workflow definition from your repo.
  2. Resolving which runners are eligible (labels, capacity, self-hosted vs hosted).
  3. Assigning the job and dispatching it to the chosen runner.
  4. Streaming logs and collecting results back.

Both hosted and self-hosted runners are clients of the same scheduling service. The only difference is where the worker process executes — Microsoft's datacenter or your machine. The job-assignment path is identical.

When step 3 fails — invalid job assignments, in the language of GitHub's post-mortem — neither side gets work. Hosted runners sit idle waiting for a job ID. Self-hosted runners connect to the control plane, fail to receive an assignment, and either error out or get rate-limited on the polling API.

[[DIAGRAM: GitHub Actions scheduling flow — repo → scheduling service → runner assignment, both hosted and self-hosted draw from the same pool]]

That's the architecture. There's no failover path. The scheduling service is a single point of failure regardless of where your workers live.

This isn't bad luck

The August 6 outage is one incident. GitHub's reliability record in 2026 is a pattern.

Per IncidentHub's tracking data, GitHub logged 26 incidents in July 2026, 23 in June, 23 in May — over 70 incidents in three months. February 2026 was worse: 37 incidents in a single calendar month, roughly 1.3 per day. April 2026 hit approximately 86% monthly uptime, and even then the platform averaged just six consecutive incident-free days at its best.

[[CHART: incidents per month on GitHub Actions platform, Feb through Jul 2026]]

These aren't all Actions outages — some are Pages, some are auth, some are webhook delivery. But the shared infrastructure underneath is the same. Copilot's coding agent, Pages builds, Actions workflows, webhook dispatch — they all flow through the same control plane. When that plane degrades, multiple product lines feel it, as August 6 demonstrated.

86% monthly uptime works out to roughly 2.5 days of downtime per month. For a CI/CD system that gates every release, that's not "occasional disruption." That's structural fragility.

What actually helps during an outage

The honest answer: nothing fully does. The mitigation playbook is diversification, not failover.

1. A second CI/CD vendor, configured but cold. GitLab CI, CircleCI, Buildkite — pick one. Keep a parallel pipeline definition checked in but not wired to your main branch's triggers. When GitHub Actions is down for over an hour, swap the trigger.

# .circleci/config.yml — kept in repo, dormant
version: 2.1
jobs:
  build:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - run: npm ci
      - run: npm run build
      - run: npm test
Enter fullscreen mode Exit fullscreen mode

2. Manual dispatch as a fallback path. GitHub's workflow_dispatch trigger still respects the scheduling service, so it doesn't help when scheduling is the broken part. But for outages limited to specific runners or regions, manual dispatch plus a local runner can unstick a release:

# Run a job locally with act when dispatch alone isn't enough
act -j build --secret-file .env.secrets
Enter fullscreen mode Exit fullscreen mode

3. Alert on the API, not the status page. The GitHub Status page is a lagging indicator. By the time it shows red, you've been red for 20 minutes. Poll the REST API from your own monitor:

# Cheap health check — fail fast if Actions is degraded
gh api /repos/{owner}/{repo}/actions/runs?per_page=1 \
  --jq '.workflow_runs[0].status'
Enter fullscreen mode Exit fullscreen mode

Wire this into PagerDuty or whatever alerting backend you already run. You'll know before GitHub's status page does — sometimes by a wide margin.

4. Treat releases as events, not cron. If your release process depends on a workflow firing at exactly T, you've coupled your ship date to GitHub's scheduling service. Decouple: gate releases on artifacts already built, with a manual approval step that's resilient to CI downtime.

The deeper lesson

The August 6 outage is a specific instance of a general rule: when you depend on a single platform, you're exposed to that platform's reliability — and reliability you can't observe, predict, or pay to upgrade.

This applies to CI/CD. It applies to model providers, to auth services, to deploy targets. Anywhere your build, deploy, or runtime depends on one vendor's control plane, you've bought a single point of failure.

That's the reason to think in layers. CI is a layer — and a replaceable one, given the alternatives above. The UI layer underneath your app is also a layer. It's the one your users actually see, and it's the one whose breakage is hardest to recover from. The platforms churn — Copilot features ship and break, Actions has good months and bad months, model APIs change pricing weekly — but the components your users touch need to behave the same regardless.

A component system that renders identically across web, iOS, and Android, with one API for all three, isn't exciting the way a new model drop is. It's the durable layer. It's what doesn't change when the scheduling service does, when the model provider throttles, when the CI vendor has a bad month.

Use GitHub Actions. Use Copilot. Use whatever model ships next week. But anchor your UI in the layer that's still here when the next nine-hour outage hits.

Top comments (0)