bootsnap: the README attributes three quarters of the speedup to path caching
Boot large Ruby/Rails apps faster
At a glance
- What is it?
- A Ruby gem that patches require and load so the load path is not rescanned, and caches compiled bytecode and parsed YAML. Useful reading is in its own caveats: the cache directory must be writable, nothing ever prunes it, one cache option is documented as breaking coverage reporting, and YAML falls back to Marshal.
- Who is it for?
- bootsnap is a load time cache with two halves, and the project's own attribution says the cheaper half does most of the work: path caching accounts for roughly three quarters of the reported gain on the largest application quoted, with compilation caching supplying the rest.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 19 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- rails/bootsnap2,742 ★ · +3
| Repository | 2026-10-04 | 2026-11-14 |
|---|---|---|
| rails/bootsnap | 2.7k | 2.7k |
3 Stars in the last 41 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The README's own numbers put three quarters of the gain in path caching
The performance section quotes three outside reports. Discourse is said to see roughly a 50 percent boot time reduction, from about 6 seconds to 3 on one machine. A smaller internal app is quoted at 50 percent, from 3.6 seconds to 1.8. Shopify's core platform, described as a rather large monolithic application, is quoted at about 75 percent faster, dropping from around 25 seconds to 6.5. The line that matters for tuning comes next: in that Shopify case about 25 percent of the gain is attributed to the `compile_cache_*` features and 75 percent to path caching, which the README calls fairly representative. So the half of the gem that only removes `$LOAD_PATH` scans is carrying most of the weight in the largest example, and the half that caches bytecode and parsed documents is the smaller contributor.
The cache directory must be writable, and nothing ever prunes it
Adding the gem is two lines, one in the Gemfile and one in `config/boot.rb` right after `require 'bundler/setup'`:
gem 'bootsnap', require: false
require 'bootsnap/setup'The ordering is deliberate. The README says it is technically possible to write `gem 'bootsnap', require: 'bootsnap/setup'` instead, but that loading it as early as possible is what produces the benefit. The operational warning comes next. Bootsnap writes to `tmp/cache`, or wherever `BOOTSNAP_CACHE_DIR` points, and that directory must be writable or Rails will fail to boot. If a writable directory is impossible, the advice is to remove the line or wrap it in a conditional, which is the read only container case. Then there is the half nobody expects: bootsnap never cleans its own cache. Purging `tmp/cache/bootsnap*` is left to the deployment, and the README states that deploys getting progressively slower are almost certainly that cause.
Nine environment variables, and two of them refuse to work together
The environment interface is a flat list of variables read by `require 'bootsnap/setup'`. `BOOTSNAP_CACHE_DIR` moves the cache, `BOOTSNAP_CONFIG` repoints the config file whose default location is `config/bootsnap.rb`, and `DISABLE_BOOTSNAP` turns the whole thing off. Two more disable halves rather than everything: `DISABLE_BOOTSNAP_LOAD_PATH_CACHE` for the load path work and `DISABLE_BOOTSNAP_COMPILE_CACHE` for both the ISeq and YAML caches. `BOOTSNAP_READONLY` is the one that matters in a container, telling bootsnap not to update the cache on a miss or a stale entry. The observability pair is where the list contradicts itself: `BOOTSNAP_LOG` sends every cache miss to standard error and `BOOTSNAP_STATS` logs hit rate statistics on exit, and the entry for the statistics variable says outright that it cannot be used while logging is enabled. `BOOTSNAP_IGNORE_DIRECTORIES` takes a comma separated list and defaults to ignoring any directory named `node_modules`.
One cache option is labelled, in the sample itself, as breaking coverage
The longer sample, aimed at people not using Rails or wanting more control, spells out the options with inline comments:
cache_dir: 'tmp/cache', # Path to your cache
ignore_directories: ['node_modules'], # Directory names to skip.
development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
load_path_cache: true, # Optimize the LOAD_PATH with a cache
compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
compile_cache_yaml: true, # Compile YAML into a cacheThe comment on the fourth option is the whole warning in one line: the bytecode cache breaks coverage reporting, which is why a project that measures coverage has to turn that specific option off rather than blame the gem. Two other details. The sample derives `development_mode` from `RAILS_ENV` with a fallback to the development string, while the environments section says all features are enabled in development, test, production and everything else according to the configuration, and that Shopify runs the gem in all environments. And the printed snippet ends after the last option without the parenthesis that closes the `Bootsnap.setup` call.
YAML and JSON are cached as MessagePack, with a Marshal fallback
The compilation half works by taking over three entry points. `RubyVM::InstructionSequence.load_iseq` is implemented to cache the result of Ruby bytecode compilation. `YAML.load_file` is modified to cache the parsed object in MessagePack format, or in Marshal when the document uses types MessagePack does not support. `JSON.load_file` is modified the same way, with no fallback mentioned. The practical consequence is that `tmp/cache` ends up holding serialized Ruby objects in two formats, one of which is Marshal, and Marshal data is executed by the interpreter that deserializes it. That makes the cache directory part of the application's trust boundary rather than a disposable scratch area, which is also why the directory has to be writable, has to survive deploys coherently, and is the reason `BOOTSNAP_READONLY` exists as a supported mode instead of only a troubleshooting switch.
require and load are patched, and the saving is the failed opens
The path half replaces scanning with a lookup table. `Kernel#require` and `Kernel#load` are modified so that instead of walking every entry on `$LOAD_PATH` looking for `x/foo.rb`, `y/foo.rb` and onward, bootsnap consults cached requirable entries per load path entry and substitutes the expanded path Ruby would have chosen. The README shows it at the syscall level, with the failing opens first:
open x/foo.rb # (fail)
# (imagine this with 500 $LOAD_PATH entries instead of two)
open y/foo.rb # (success)and then the cached behaviour, a single open. `Bootsnap::LoadPathCache` builds that list on initialization or when the path changes, scanning fully only when the cache cannot answer, and the work is described as a minor evolution of bootscale. Cache behaviour is observable through a callback set on `Bootsnap.instrumentation`, whose event is `:hit`, `:miss`, `:stale` or `:revalidated`, with `Bootsnap.log!` as the shortcut and nil to switch it off. Spring sits alongside all of this: it keeps a pre-booted process instead of caching file loads, and the two are called orthogonal.
v1.24.6 and v1.25.0 were published twenty three seconds apart
The release record shows two tags landing together on 2026-08-08, v1.24.6 at 06:58:18 and v1.25.0 at 06:58:41, followed by v1.26.0 on 2026-09-04, and the last push to the default branch is dated 2026-09-19. A patch and a minor version published in the same batch is the pattern of a fix that also carried a feature, and it means the two numbers in a Gemfile lock do not map to two separate decisions. The tree explains what the gem is made of: `ext/` for the native extension that makes the caching cheap, `lib/` and `exe/`, a `bin/`, a Rakefile, the gemspec, a `.rubocop.yml`, a `.devcontainer/` and `test/`. Two naming details are worth knowing if you script around the repository: the licence file is `LICENSE.txt` rather than the conventional bare `LICENSE`, and the documentation lives in `CHANGELOG.md`, `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md` at the root rather than in a `docs/` directory.
Editorial conclusion
bootsnap is a load time cache with two halves, and the project's own attribution says the cheaper half does most of the work: path caching accounts for roughly three quarters of the reported gain on the largest application quoted, with compilation caching supplying the rest. That makes it a good fit for a large monolith with a deep load path and a poor fit for a small service that boots in a second already, and for read only container images unless you set BOOTSNAP_READONLY and mount a writable tmpdir. Two operational facts deserve a decision before rollout. Nothing ever prunes the cache, so a stale tmp/cache/bootsnap tree is the first thing to suspect when deploys slow down over time, and the YAML path can fall back to Marshal for types MessagePack cannot represent, which means the cache directory holds data your interpreter will deserialize and therefore has to be as trusted as the application itself.
Frequently asked questions
What does the rails/bootsnap gem do for a Ruby application?
It is a library that plugs into Ruby, with optional YAML and JSON support, to optimize and cache expensive computations. The work falls into path pre-scanning, which modifies Kernel#require and Kernel#load to remove load path scans, and compilation caching for bytecode, YAML and JSON. It works on macOS and Linux.
Where does bootsnap write its cache and who cleans it up?
It writes to tmp/cache, or to the path in BOOTSNAP_CACHE_DIR, and that directory must be writable or Rails will fail to boot. Bootsnap never cleans its own cache, so the README says you may need to purge tmp/cache/bootsnap* periodically and names that as the likely cause of deploys getting progressively slower.
Which environment variables does bootsnap read?
BOOTSNAP_CACHE_DIR, BOOTSNAP_CONFIG, DISABLE_BOOTSNAP, DISABLE_BOOTSNAP_LOAD_PATH_CACHE, DISABLE_BOOTSNAP_COMPILE_CACHE, BOOTSNAP_READONLY, BOOTSNAP_LOG, BOOTSNAP_STATS and BOOTSNAP_IGNORE_DIRECTORIES. BOOTSNAP_STATS cannot be used while BOOTSNAP_LOG is enabled, and BOOTSNAP_IGNORE_DIRECTORIES defaults to ignoring any directory named node_modules.
How does bootsnap differ from the Spring gem?
The README calls them orthogonal. Bootsnap speeds up the loading of individual source files by caching path lookups and compilation, while Spring keeps a copy of a pre-booted Rails process so parts of the boot process can be skipped entirely. The two are said to work well together.
What format does bootsnap store cached YAML and JSON in?
YAML.load_file results are cached in MessagePack format, falling back to Marshal when the document uses types MessagePack does not support, and JSON.load_file results are cached in MessagePack. Bytecode compilation results are cached separately through RubyVM::InstructionSequence.load_iseq.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/rails-bootsnap)Star history chart for your README
[](https://hysenlabs.com/projects/rails-bootsnap)Was this useful?