# Spring: the Rails preloader that keeps your app booted between test runs

> Spring is a Rails application preloader that keeps the app running in the background so tests, rake tasks and migrations skip the boot cycle. It is for Ruby 3.1+ and Rails 7.1+ projects on platforms that support Process.fork.

**rails/spring** — Rails application preloader

- Repository: https://github.com/rails/spring
- Stars: 2,815 · Forks: 342
- Language: Ruby
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/rails-spring

## What Spring removes from the Rails development loop

Every Rails command normally pays the same fixed cost: load the framework, load the gems, run initializers, connect to the database. That cost is identical whether you are running one test file or the whole suite, and it is the reason a single controller test can take seconds while the test body itself takes milliseconds. Spring is a Rails application preloader. It keeps your application running in the background, so you don't need to boot it every time you run a test, rake task or migration, as the README puts it.

The audience is narrow and specific. You need MRI 3.1 or newer, Rails 7.1 or newer, and Bundler v2.1 or newer. You also need a platform where Process.fork exists. Spring makes extensive use of Process.fork, so it will not provide a speed up on Windows or JRuby. That is not a configuration problem you can work around; it is the mechanism itself.

## How the preloader decides what to reload and what to restart

Spring runs two layers of process. A server process owns the application, and short-lived commands attach to it. The README's own walkthrough shows bin/spring status reporting a spring server line and one or more spring app lines, each tagged with an environment such as test mode or development mode. Different environments get their own app process, which is why running bin/rake routes after a test boots a second process rather than reusing the first.

Reloading is delegated to Rails rather than reimplemented. Spring reloads application code, and therefore needs the application to have reloading enabled. Edit a model or a test file and the change is picked up on the next run without restarting the background process. Edit something used to start the application, such as a config, an initializer or the Gemfile, and Spring restarts the app automatically. The README demonstrates this by touching config/application.rb and showing the app process uptime reset to 1 sec ago.

The interesting design decision is that Spring will let you turn that safety net off. Setting Spring.dangerously_allow_disabling_reloading = true in config/spring.rb runs Spring without Rails' in-process reloaders. The documentation is explicit that this is advanced and risky and that you must then guarantee every reloadable resource materializes lazily in the forked child, not in the server process. It suggests boot-time assertions inside Spring.after_environment_load, for example raising if Rails.application.routes_reloader.loaded is true or if I18n.backend has translations loaded. The flag name is honest about the trade-off, and the README's own advice is to leave it disabled when unsure.

## Installing Spring and running a first test

Spring goes in the development group of your Gemfile. The README warns that installing it from a git source is not a supported way of using Spring, so use the released gem.

```ruby
gem "spring", group: :development
```

After bundle install, the recommended step is to springify the executables in bin/. This generates a bin/spring executable and inserts a small snippet into the relevant existing executables.

```bash
bundle install
bundle exec spring binstub --all
```

That snippet wraps execution in a begin/rescue LoadError block that loads ../spring relative to the executable. On platforms where Spring is installed and supported, it hooks Spring into the command. Elsewhere the LoadError is swallowed and the lines after it run as normal, which is why the same bin/ files stay portable.

Before the first run, confirm reloading is on. In config/environments/test.rb, config.enable_reloading must be true. On Rails versions before 7.1 the setting is named cache_classes and must be false.

Now run a test through the binstub. The first run boots the application, so it is not fast, and the output line Running via Spring preloader in process <pid> tells you Spring handled it.

```bash
time bin/rake test test/controllers/posts_controller_test.rb
bin/spring status
```

The status command should list a spring server and a spring app in test mode. The second run of the same command reuses that process and skips the boot. If you do not want to type bin/ in front of every command, the README points at direnv: create an .envrc containing PATH_add bin so ./bin lands on your PATH when you cd into the app. Spring shuts down when you close the terminal, and bin/spring stop forces it.

## The reloading-disabled path is a footgun, not a feature

The most consequential limitation is not the fork requirement, because that one announces itself immediately. It is the reloading-disabled mode. Turning it on removes the guarantee that stale constants and boot-time state cannot leak into a forked child. Spring's own mitigation is a set of assertions you write yourself, and those assertions only cover the resources you thought to check. A route drawn at boot or an i18n backend populated early will not fail loudly unless you added the matching raise.

There is a second, quieter failure mode: the preloader can mask boot-order bugs. An initializer that only works because something else ran first may appear fine under Spring for weeks and then fail on a cold boot in CI or production. Spring is a development tool, and the README is blunt that you must not install it on your production environment. Treat any behaviour that only reproduces under Spring as suspect until you have reproduced it with the preloader stopped.

Spring is also the wrong tool when your bottleneck is not boot time. If a single test file takes thirty seconds because of slow factories or network calls, shaving the framework boot changes little. And if your team runs commands once or twice a day, the extra process management is not worth the mental overhead.

## Spring versus Bootsnap and a plain bundle exec

The nearest alternative in a Rails project is Bootsnap, which attacks the same boot cost from a different direction. Bootsnap caches compiled Ruby bytecode and expensive path lookups on disk, so each cold process starts faster but still starts. Spring avoids the start entirely by keeping a process alive and forking it. The two are complementary rather than competing: Bootsnap speeds up the first run and any command that bypasses Spring, while Spring speeds up the second and subsequent runs.

The other alternative is doing nothing and accepting bundle exec rake test as it is. That is a legitimate choice. It has no background processes to reason about, no stale-state class of bug, and no need for assertions about what loaded at boot. Spring's value proposition is entirely about repetition: the README's walkthrough shows the same test command dropping from roughly two seconds on the first run to well under a second once the app is warm. If your workflow does not look like that, the trade is not in your favour.

## Maintenance, upgrades and the MIT licence

The repository is not archived, and the last push was on 2026-06-30. Releases have been frequent through 2026: v4.5.0 on 2026-05-01, v4.6.0 on 2026-05-26 and v4.7.0 on 2026-06-23. Spring is MIT licensed, which places few restrictions on commercial use; the LICENSE.txt file at the repository root is the authoritative text, and anything beyond that is a question for your legal team rather than for this article.

Upgrade cost is mostly a function of the Rails version you run, not Spring's own changelog. The reloading setting rename from cache_classes to enable_reloading landed in Rails 7.1, and Spring's compatibility floor is now Rails 7.1 and Ruby 3.1. Projects on older Rails need to stay on an older Spring. Because Spring is a development-only dependency, an upgrade cannot break production, but it can break the binstubs: after bumping the gem, re-run bundle exec spring binstub --all so the generated snippet matches the installed version. Deployment hygiene is a two-command concern, and the README spells it out.

```bash
bundle config set without 'development test'
bundle install
```

Run that on the production machines so the gem is never installed there. Removal is symmetric: bin/spring binstub --remove --all, then drop the gem from the Gemfile.

## Conclusion

Adopt Spring if you run tests, rake tasks or migrations repeatedly on MRI and want to stop paying the boot cost each time. Skip it if you are on Windows or JRuby, where Process.fork is unavailable, or if you have configured your app to load reloadable resources at boot and cannot satisfy the assertions Spring's documentation suggests. Before rolling it out, verify three things: that config.enable_reloading is true in the test environment (cache_classes must be false on Rails before 7.1), that bin/spring binstub --all ran cleanly, and that bundle config set without 'development test' is in place for production so the gem never ships there.

## FAQ

### How do I install Spring in a Rails application?

Add gem "spring", group: :development to your Gemfile, run bundle install, then run bundle exec spring binstub --all to generate the bin/spring executable and hook the existing binstubs. Installing from a git source is not a supported way of using Spring.

### Does Spring work on Windows or JRuby?

No. Spring makes extensive use of Process.fork, and the README states it will not be able to provide a speed up on platforms that do not support forking, naming Windows and JRuby specifically.

### How do I stop the Spring background process?

You normally do not need to. Spring shuts down automatically when you close your terminal, but bin/spring stop performs a manual shutdown and prints Spring stopped.

### Why does Spring need config.enable_reloading set to true?

Spring reloads application code, so the application must have reloading enabled in the environments Spring manages, including test. On Rails versions before 7.1 the setting is called cache_classes and must be false.

### Should Spring be installed in production?

No. The README states you must not install Spring on your production environment, and recommends running bundle config set without 'development test' before bundle install on production machines.

## Sources

- [Issues](https://github.com/rails/spring/issues)
- [License: MIT](https://github.com/rails/spring/blob/main/LICENSE)
- [rails/spring on GitHub](https://github.com/rails/spring)
- [README](https://github.com/rails/spring/blob/main/README.md)
- [Releases](https://github.com/rails/spring/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rails-spring
