How does GitHub's new Git storage handle 7,200 pushes per second?
GitHub has replaced the storage layer that underpins every repository on its platform. The new system serves thousands of writes per second while keeping read latency flat.
GitHub moved 99.99999 percent of repositories onto Azure Blob Storage last year. That migration was the bridge. The new architecture, built on top of it, is designed for a different traffic pattern: agents pushing code around the clock.
Internal testing showed 5,000 concurrent agents sending 7,200 pushes per second to a single repository. Read traffic scaled at the same time with consistently low latency. The company expects sustained write rates to replace today's short bursts within the next year.
Two principles shaped every component
Minimize coordination. Decouple storage from compute. Git objects are immutable and named by their contents. Two writers can store objects simultaneously without agreeing on anything. Only the reference update — the pointer that says "branch main now points at commit abc123" — needs consensus. That update is tiny compared to the pack data.
The legacy architecture coupled storing data, serving requests, and maintaining repositories. The new design splits those concerns so each scales independently. Developers still push, fetch, and branch the same way. Branch protections and other safeguards remain in place.
Virtual replicas replace fixed replica sets
Spokes continues to handle routing, load balancing, and the internal API used by the rest of GitHub. Underneath Spokes, a fleet of virtual replicas serves reads, writes, or both. A virtual replica's local disk is a cache that can be rebuilt at any time.
The service routes a repository's traffic to the same virtual replicas when possible. Warm caches mean low latency. Correctness does not depend on any particular replica. When one disappears, the others keep serving. A replacement fills its cache as traffic arrives. When a repository gets busy, more virtual replicas are added.
Durable state lives in two places. Git objects — packs, indexes, reachability bitmaps, multi-pack indexes, and cached fetch responses — sit in Azure Blob Storage. References and the pack manifest live in a separate replicated log service called Ref-CAS.
Ref-CAS orders reference updates without holding objects
Each reference update in Ref-CAS is a compare-and-swap committed by a quorum. A transaction locks only the references it touches, verifies each still has the expected value, and becomes visible once a majority of log replicas record it. Agents mostly push to their own branches, so most updates do not contend and replicate in parallel.
Every commit receives a log sequence number. The repository's version is the LSN of its latest commit. A read starts with a version check. The virtual replica sends the version it already holds. If that version is current, the reply carries no references at all. An unchanged repository costs a single comparison.
If the replica is behind, it receives the current references, the new version, and pointers to the pack data it needs. Ref-CAS never holds or validates object data. The virtual replica uploads a push's pack to Blob Storage before committing the reference transaction. A reference only ever points at objects that are already durable.
This design delivers tens of thousands of reference operations per second. It also provides a baseline for annotations that help reads decide what to download for a given request.
Compaction runs on dedicated nodes, not on serving replicas
A push adds a pack to Blob Storage. Git can serve a repository from many packs, but each additional pack makes lookups slightly more expensive. The pack count cannot grow without bound.
In the legacy architecture, compaction ran on the same replicas that served traffic and had to run on every replica. In the new architecture, compaction nodes work in the background directly against Blob Storage and Ref-CAS. They never run on a virtual replica, so CPU and memory stay dedicated to serving requests.
A compaction node claims a shard via a lease in Blob Storage. While virtual replicas keep uploading packs and listing them in the manifest, the node reads the manifest, collects new packs into an open segment, and seals the segment when it reaches a defined limit based on pack count, size, or age. It then combines the packs into fewer, larger ones.
Before publishing, the node verifies the new packs contain exactly the same objects as the inputs. It uploads the new packs, adds them to the manifest, and a second transaction removes the inputs. Cleanup deletes them later. Each pack's hash maps to a shard. Adding shards and nodes adds compaction throughput. A compacted pack still under the size limit goes back into a shard by its own hash and merges again in tiers until it reaches the limit.
The result is an effectively flat pack count while pushes climb. Garbage collection — the step that removes objects nothing references anymore — works similarly with dedicated workers and has special behaviors for safe removal.
What the rollout timeline looks like
The architecture has been running underneath GitHub while the platform keeps operating. Repositories are being onboarded to the new system. The goal is for every repository, from a side project to the busiest monorepo, to run on the same architecture with no special handling for the busy ones. The company has not published a completion date for the migration.
The sources this article was written from
- GitHub Blog
https://github.blog/engineering/architecture-optimization/inside-githubs-new-git-storage-architecture/