A Small Bake-Off for Unreliable GitHub Scheduling
On August 18, a blog post I had scheduled for that morning wasn't there.
It was my post about AI-designed genomes and biosecurity, which I had spent quite a bit of time on (and, alas, thought might get more attention than it ultimately did).
My site is static, so publishing a future-dated post requires a new production build after 7:00 a.m. New York time on its publication date. I had a scheduled GitHub Action doing that every morning at 7:17.
The site hadn't failed to build. GitHub just hadn't started the job when I expected it to.
GitHub was also having a rough August, including several problems with Actions. I can't tie any particular GitHub incident to any particular delay I saw, and I don't need to. For this job, what matters is whether the scheduler starts reasonably close to the requested time.
So I ran a small bake-off.
The Bake-Off
I left GitHub scheduled at 7:17 and added Cloudflare Cron at 7:05. Both ran after the site's 7:00 publication boundary and both triggered the same Cloudflare Pages deployment, so I was comparing schedulers rather than deployment platforms.
I ran them side by side until I had ten comparable dates, August 19 through August 28, recording when each scheduler was supposed to run, when it actually started, and how long the resulting deployment took to reach verified success. I also checked for duplicate or overlapping builds and kept GitHub's manual trigger as a fallback.
Cloudflare Cron uses UTC, so keeping 7:05 New York time across daylight saving time required two possible UTC schedules and a small local-time check. Mildly annoying, but straightforward.
The Result
| Measure | Cloudflare Cron | GitHub scheduled event |
|---|---|---|
| Minimum recorded delay | 0.248 seconds | 17 minutes 42 seconds |
| Median recorded delay | 0.301 seconds | 25 minutes 56 seconds |
| Maximum recorded delay | 0.370 seconds | 10 hours 5 minutes 45 seconds |
| Median deployment verification | 1 minute 20 seconds | 1 minute 24 seconds |
It was not close.
GitHub's timing includes a little workflow setup that Cloudflare's does not. That does not explain a 26-minute median or a ten-hour outlier.
Once either scheduler triggered Cloudflare Pages, deployment time was essentially the same. The scheduler was the difference.
Cloudflare started within four-tenths of a second of schedule on every comparable date. GitHub's best result was more than 17 minutes late, its median was about 26 minutes late, and one run was more than ten hours late.
GitHub documents that scheduled Actions can be delayed under load. Fine. For deadline-sensitive work, this is unreliable infrastructure.
I use GitHub constantly. I like GitHub. I pay for GitHub. GitHub needs to fix this shit.
The Switch
The operational choice was Cloudflare Cron for automatic, deadline-sensitive publication, with GitHub Actions retained as a manual fallback.
I made the change permanent on August 30. The Cloudflare Worker now handles the 7:05 a.m. New York run, using paired UTC schedules and a local-time check for daylight saving time. The automatic GitHub schedule was removed.
The cutover changes only the scheduler. Deployment still has to reach a verified terminal state, credentials remain narrowly scoped, and GitHub remains available for manual recovery.
Ten comparable dates were sufficient for this operational decision, not for a general claim about provider reliability. I may update the post after the permanent configuration has accumulated more production history.