GitHub Actions Self-Hosted Runner Raspberry Pi 5 Docker Build Cache Hit Rate

Build times don’t lie.
When a Reddit thread in early 2026 documented a 6x reduction in GitHub Actions build times after switching to self-hosted runners, it triggered a wave of engineers asking the same question: can an $80 Raspberry Pi 5 running a self-hosted runner actually compete with GitHub’s hosted infrastructure β especially when Docker build cache hit rate is the real metric that moves the needle?
The short answer is yes. But the details matter a lot.
This isn’t about hobbyist tinkering. The self-hosted runner on Raspberry Pi 5 story is fundamentally a cost-performance story playing out in production environments where teams pay GitHub per-minute for hosted runners while their Docker layers rebuild from scratch on every push.
The core insight: Persistent local storage on a Raspberry Pi 5 enables Docker layer cache retention across runs β something hosted runners can’t match without external cache backends. Teams reporting 6x build time reductions aren’t doing anything exotic. They’re just keeping their cache alive.
Four things this analysis covers:
- Why GitHub-hosted runners have a structural cache hit rate disadvantage
- What the Raspberry Pi 5’s hardware actually delivers for Docker workloads
- How to measure and improve your cache hit rate in practice
- When self-hosted makes economic sense β and when it doesn’t
Why Cache Hit Rate Became the Battleground
GitHub Actions launched self-hosted runners in 2019, but they stayed niche for years. Most teams took the path of least resistance: hosted runners, billed per minute, zero infrastructure overhead. That calculus held up until Docker-heavy workflows became the norm.
The problem is structural. GitHub-hosted runners are ephemeral. Every job spins up a fresh VM. Your Docker build cache β those layers representing hours of accumulated apt-get install, pip install, and compiled dependencies β gets wiped after every single run. Without an external cache backend like GitHub’s Cache action or a registry-based cache-from, you’re rebuilding from scratch constantly.
GitHub introduced the actions/cache action and later cache-to/cache-from with registry targets to address this. Both approaches add latency (uploading and downloading cache archives over the network) and have size limits. The GitHub Actions cache sits at a 10 GB per-repository cap as of 2026, and entries expire after 7 days of inactivity.
The Raspberry Pi 5 entered this picture seriously in late 2023 with its RP1 southbridge chip, BCM2712 processor, and β critically β PCIe 2.0 support for NVMe drives. Pair one with a 500 GB NVMe and you have persistent, fast local storage where your Docker layer cache lives indefinitely. No expiration. No upload/download roundtrip. No size cap beyond your disk.
That’s when the cache hit rate equation changed.
The Cache Hit Rate Arithmetic
Docker’s build cache works by hashing each instruction and its context. If nothing changed, it reuses the cached layer. On a persistent runner, that cache accumulates across every build. On an ephemeral hosted runner, it resets every time.
Real-world cache hit rates on persistent self-hosted runners consistently land between 70β85% for mature projects with stable dependency trees. On ephemeral hosted runners without external cache, you’re effectively at 0% for layers below your application code β the expensive layers. With GitHub’s registry cache backend, teams typically recover 40β60% cache hit rate, but at the cost of 60β120 seconds of network I/O per run for pulling and pushing cache manifests.
The Reddit r/github thread from early 2026 documented a team achieving 6x build time reduction after moving to self-hosted. Their specific workflow: a Node.js + Docker project where npm install alone was taking 4 minutes on hosted runners. With a persistent cache on a local runner, that step dropped to 18 seconds. The math tracks β an 85% cache hit rate on a dependency install layer that takes 4 minutes cold means you’re spending roughly 36 seconds on average instead of 4 minutes.
What the Raspberry Pi 5 Actually Delivers
The Pi 5 with a NVMe hat hits sequential read speeds around 900 MB/s on a decent PCIe Gen 2 M.2 drive. Docker layer cache reads are mostly random small I/O, but even there the Pi 5 outperforms SD card setups by 10β20x. This matters because docker buildx cache operations on complex images involve hundreds of small file reads.
CPU performance is the real constraint. The BCM2712 is a 2.4 GHz quad-core Cortex-A76. For builds that are CPU-bound β think compiling native extensions, running TypeScript compilation across 500k lines, anything that pegs all cores β the Pi 5 will lose to GitHub’s hosted runners, which provision 2 vCPUs of a modern Xeon. For I/O-bound workflows, the persistent cache advantage dominates.
There’s another angle worth noting. When the Pi 5 is the deployment target β IoT, edge, home lab β the self-hosted runner collapses build and deploy into a single pipeline step. No container registry needed as an intermediary. Teams deploying FastAPI apps to Raspberry Pi hardware via self-hosted runners, documented in several production write-ups from 2025, report this architecture simplification as equally valuable to the cache gains.
Measuring Your Actual Cache Hit Rate
Most teams don’t know their cache hit rate. They should. docker buildx build with --progress=plain outputs cache hit/miss per layer. Parse that output in CI and track it over time.
A simple shell approach:
docker buildx build --progress=plain . 2>&1 | grep -c "CACHED"
docker buildx build --progress=plain . 2>&1 | grep -c "RUN\|COPY\|ADD" | grep -v CACHED
The ratio gives you layer-level cache hit rate. Teams running this on persistent self-hosted runners report stabilizing above 75% within two weeks of consistent builds. Teams on ephemeral runners without registry cache report under 10%.
Hosted vs. Self-Hosted: The Honest Comparison
| Criteria | GitHub-Hosted (no cache) | GitHub-Hosted + Registry Cache | Pi 5 Self-Hosted (NVMe) |
|---|---|---|---|
| Docker cache persistence | None (0%) | Partial (40β60% hit rate) | Full (70β85% hit rate) |
| Cache setup complexity | None | Medium (registry config) | Low (automatic via disk) |
| Cache I/O overhead per run | None | 60β120s network roundtrip | ~5s local NVMe read |
| Hardware cost | $0 upfront | $0 upfront | ~$120β150 (Pi 5 + NVMe) |
| Per-minute billing | Yes (~$0.008/min, Linux) | Yes | No |
| Build time (I/O-bound, mature project) | Baseline | ~1.5β2x faster | ~4β6x faster |
| Best for | Simple, infrequent builds | Teams avoiding infra overhead | High-frequency Docker builds |
The trade-off isn’t Pi 5 vs. GitHub’s hosted runners in a vacuum. It’s about build frequency. At fewer than 20 Docker builds per day, the economics barely move. Above 50 builds per day on I/O-heavy workflows, the advantage compounds quickly β both in time saved and in GitHub Actions minutes not consumed.
Three Scenarios Where This Decision Actually Gets Made
Scenario 1 β Monorepo with frequent pushes. A team pushing to a monorepo 80 times per day with a 10-minute Docker build on hosted runners spends roughly 13 hours of runner time daily. At GitHub’s Linux runner rate ($0.008/min), that’s ~$6.24/day or $190/month. A Pi 5 setup at $130 breaks even in under a month. The cache hit rate improvement means those 10-minute builds drop to under 3 minutes, which compounds the savings further.
Scenario 2 β Edge deployment target. When the Pi 5 is the deployment target, the self-hosted runner collapses build and deploy into one pipeline step. The cache advantage here is secondary to the architecture simplification.
Scenario 3 β CPU-bound compilation workloads. Building a Rust project from scratch or compiling a large C++ codebase? The Pi 5’s Cortex-A76 cores will feel the pinch. GitHub’s hosted 2-vCPU runners on modern Xeon silicon will beat it on cold builds. Self-hosted makes sense only if your cache hit rate is high enough that cold builds are rare.
This approach can fail when teams underestimate maintenance overhead. A Pi sitting in a closet running 24/7 needs monitoring, disk management, and occasional restarts. That’s not zero cost β it’s just a different kind of cost.
What the Next 12 Months Look Like
GitHub is reportedly expanding its larger runner tiers and experimenting with persistent cache across runner pools. If GitHub introduces native persistent-layer caching for standard hosted runners in late 2026 or 2027, it closes the gap significantly. Until then, the local NVMe advantage holds.
ARM64-native Docker images are becoming more common, which will further benefit the Pi 5’s native architecture. Multi-node Pi clusters running as GitHub Actions runner pools are already appearing in open-source project CI configurations.
Key Takeaways
- Persistent NVMe cache is the core advantage β not raw compute power
- 70β85% cache hit rates on self-hosted persistent runners vs. near-zero on ephemeral hosted runners without a registry cache backend
- Break-even on a $130 Pi 5 setup lands under 30 days for teams running 50+ Docker builds daily on GitHub-hosted minutes
- CPU-bound workloads remain a weakness β the Pi 5 isn’t competing with cloud vCPUs on raw compilation speed
- If your cache hit rate sits below 60%, either your Dockerfile layer ordering is off or your cache isn’t persisting between runs β fix that first
The bottom line: a $130 piece of hardware with a well-structured Dockerfile and an NVMe drive can genuinely outperform $50/month in cloud CI spend β for the right workload. This isn’t a niche experiment anymore. It’s a legitimate cost-performance decision.
What’s your current Docker cache hit rate on CI? If you haven’t measured it, that’s exactly where to start.
References
- GitHub Actions Self-Hosted Runner: A Complete Guide to Private Environment Deployment Β· BetterLink B
- Deploying FastAPI on Raspberry Pi using GitHub Actions (SelfβHosted Runner) | by Kumar Shishir | Med
- r/github on Reddit: We cut GitHub Actions build times by 6x with self-hosted runners β sharing our s
Photo by Rubaitul Azad on Unsplash


