Rails World '26: The RubyGems Compact Index
I was in the audience at Rails World 2026 for Jenny Shen’s talk on the RubyGems Compact Index, the last technical talk before the closing keynote. Jenny is a Senior Developer at Shopify and a RubyGems.org maintainer, and she opened by asking the room who had heard of the compact index before. Not many hands went up, which was kind of the point: it’s a piece of infrastructure that handles tens of millions of requests a day, and most Rubyists never think about it.

Every time Bundler resolves your Gemfile, it’s talking to the compact index. In this post, we’ll walk through what the compact index actually is, how RubyGems.org keeps it in sync at scale, and two features Jenny helped ship in the past year to make gem installs faster and more secure.
The Three Endpoints Behind Every Bundle Install
The compact index is the API behind RubyGems.org that Bundler and the gem command actually talk to when they need to know what’s out there. It
isn’t a REST API in the CRUD sense, it’s three plain-text endpoints, each one designed to be as cheap as possible to generate, cache, and diff.
The first, /names, is the simplest: a flat list of every gem name that has ever been pushed to RubyGems.org, one per line. Bundler uses it mostly
to answer “does this gem exist at all.”
The second, /versions, is where the interesting part starts. For every gem, it lists every version that has been released (yanked versions show up
too, prefixed with a -), plus an MD5 hash Jenny called the info checksum.
That checksum is a fingerprint of the gem’s /info file, the third endpoint, and it’s a big part of why the compact index scales the way it
does: Bundler fetches the (tiny) versions file, compares checksums against what it already has cached locally, and only re-downloads the /info
file for a gem when something about it actually changed.
That third endpoint, /info/{gem_name}, is where the dependency data lives. For every released version of a gem, it lists the other gems it depends on and their version constraints, the required Ruby and RubyGems versions,
and a SHA-256 checksum of that specific .gem file.
That SHA-256 checksum matters more than it sounds: it’s what lets Bundler verify, when you run bundle install, that the gem it downloaded is the one RubyGems.org says it should be, and it’s the same checksum that ends up recorded in your Gemfile.lock if you’re using Bundler’s checksum verification.
Bundler keeps its own local cache of all three (/names, /versions, and /info) and only re-fetches when your Gemfile.lock is missing, out of date, or references a gem it
doesn’t have cached yet.
Multiply that across the Ruby ecosystem: Jenny said RubyGems.org hosts more than 200,000 gems, and these three endpoints together serve more than
50 million requests a day. Three plain text files, effectively, carrying the entire dependency graph of every Rails app currently
running bundle install somewhere.
Keeping a text file in sync at that scale
None of this would hold up if RubyGems.org had to hit the database and build /versions fresh for every bundle install. Instead it’s a plain, appendable text file: for most of the
month, new gem releases just get tacked onto the end, which is what makes it cacheable in the first place.
RubyGems.org recompacts it once a month, folding old versions of a gem down to a single line and dropping anything that’s been yanked in the meantime.
Both /versions and /info/{gem_name} support ETags and HTTP Range requests, so a client that already has most of the file cached only has to
ask for the bytes it’s missing, not redownload the whole thing on every bundle install.
That’s the other half of the scaling story alongside the checksum diffing: a simple, append-friendly text format on one side, and an HTTP-savvy client on the other, meeting in the middle at 50 million requests a day.
Cooldown: A New Security Feature in Bundler
The rest of Jenny’s talk was about what you can build once that index exists, and the first example was cooldown. The security problem is simple:
An account gets compromised, a malicious version ships, and any bundle install in the minutes that follow resolves straight to it.
A cooldown lets you tell Bundler to skip any version that hasn’t been public for at least a few days, which buys the community time to notice something is wrong before your app installs it.
You can set it per source in your Gemfile:
source "https://rubygems.org", cooldown: 7
Or at the project or global level, without touching the Gemfile at all:
bundle config set cooldown 7
bundle config set --global cooldown 7
There’s also a flag for one-off runs, bundle install --cooldown 7 or bundle update --cooldown 7, and an environment variable, BUNDLE_COOLDOWN,
for CI.
Seven days is the number RubyGems.org itself recommends as a starting point, and it’s the example Jenny used on stage, citing incidents like the Axios npm compromise as exactly the kind of window this is meant to close.
It only works because the compact index now carries the data to support it. RubyGems.org added a per-version created_at timestamp to the /versions
payload through a dual-write migration, so cooldown didn’t require a new endpoint, just a new field on the one that already existed.
Content-Addressable Gems: Precompiled Binaries Without the Bloat
Jenny’s second example started from an idea Aaron Patterson pitched on the On Rails podcast earlier this year: a gem with native extensions installs faster when it doesn’t have to compile from source, but the usual way to ship a precompiled gem is a single “fat” binary that bundles every supported Ruby ABI (Application Binary Interface, essentially each Ruby version and platform combination), and that file keeps growing with every new Ruby release.
Content-addressable gems publish one skinny binary per ABI instead, each one named with a hash of its own contents (that’s the “content-addressable” part):
nokogiri-1.18.9-x86_64-linux.gem # traditional platform gem, every ABI
nokogiri-1.18.9-78be552b.gem # content-addressable gem, one ABI
This isn’t just an idea Jenny covered secondhand. She’s a co-author, alongside Shopify colleagues Harriet Oughton and Gira Chawda, on the pull request that added RubyGems and Bundler client support for building, installing, displaying, yanking, and locking these gems, merged into RubyGems on September 6, 2026.

On the publishing side, she pointed to Shopify’s cibuildgem , a tool that compiles a gem’s native extensions
directly on each target platform through CI, rather than cross-compiling inside Docker the way rake-compiler-dock does. Shopify’s own numbers are
the pitch for bothering with any of this: a fresh Rails app’s bundle install takes around 25 seconds on a MacBook Pro M4, and roughly 80% of that
is native extension compilation. Skip the compilation, and it drops to about 5 seconds.
A handful of Shopify’s own gems (stack_frames, heap_profiler, rubydex) already ship precompiled through cibuildgem. Harriet Oughton, the same
engineer who co-authored the RubyGems pull request, also added support for packaging single-ABI gems to cibuildgem, so the two projects now produce
and consume the same shape of artifact.
Try RubyGems 4.1.0 Beta
Jenny closed with a straightforward ask: go try the beta. Content-addressable gems shipped in
RubyGems and Bundler 4.1.0.beta1 , released September 9, 2026, the same beta that
replaces Molinillo with PubGrub as the dependency resolver and extends compact index support to more gem commands.

gem update --system --pre
# or, for an existing app
gem install bundler --pre
bundle update --bundler=4.1.0.beta1
Conclusion
Cooldown isn’t brand new by the time you read this. It shipped in Bundler 4.0.13 back in June, and it’s one piece of a much bigger effort: Ruby Shield , the four-year partnership between Ruby Central and Shopify, backed by $1 million and dedicated engineering time, that also gave RubyGems.org mandatory MFA for popular gems, trusted publishing, and lockfile checksums. Listening to Jenny, what stuck with me is how much of that work stays invisible until a talk like hers walks you through it.
AI has made supply chain security more urgent, not less. In May and June 2026, autonomous agents bearing markers associated with OpenAI tooling uploaded thousands of malicious packages to RubyGems.org , using shared Ruby infrastructure to run code and probe for API keys. RubyGems’ own investigation couldn’t confirm the source of the attack, but it’s the kind of campaign cooldown is built to slow down: a few days of delay before a fresh gem gets adopted, long enough for the community to notice before your bundle install does.
Interested in securing your production environments with a trusted partner? Check out our infrastructure assessment .