Ruby

bundle exec be gone

Tired of `bundle exec`? Run your ruby CLI tools without `bundle exec` thanks to the power of binstubs.

Wolfgang Rittner

updated August 21, 2025 · 4 min read

Why do we need to use bundle exec in the first place?

[bundle exec] executes the command, making all gems specified in the Gemfile(5) available to require in Ruby programs.

If you have multiple versions of the same gem installed, say you've got both rails 7 and rails 8 installed into your global set of gems, and then run rails server in your terminal, your shell does not know which version's rails executable you want it to run. Rather it will run just one of them, and not necessarily the one you intended, which will lead to random, weird and unexpected results.

bundle exec makes sure that everything that runs and is required is using the right gems in the version specified by your project's Gemfile and Gemfile.lock respectively. That sounds great!

But I have to type bundle exec in front of any ruby command line tool I want to run? That's silly!

gemsets

One way to fix that is using gemsets. Ruby gemsets are isolated environments that let you manage different gem versions for separate projects, preventing conflicts. They’re typically used with tools like RVM or rbenv to keep project dependencies clean and consistent. Now, with just the gems installed used by your project in the gemset you are using, you could omit the bundle exec.

But gemsets come at a price. Gems need to be installed multiple times, because they are installed for each project individually, which slows things down. You need to keep the gemsets clean with only one version of each gem being installed and present in the gemset, otherwise you'd run into the same issues outlined above.
They are one more thing to manage.

I was using gemsets for a long time before switching to asdf and then to mise-en-place lately, yet I didn't want to give up on the benefits. And I wanted something that worked better and was less effort to maintain. I wanted my cake, and I wanted to eat it!

gemsets not supported

When I switched to asdf and mise-en-place for managing and installing my rubies, I reconsidered using gemsets, because they are not supported by these tools out-of-the-box. I wanted to give a gemset-less existence a try.
But I also didn't want to go back to bundle exec.

meet bundle binstubs

Binstubs are scripts that wrap around executables. Bundler creates a small Ruby file (a binstub) that loads Bundler, runs the command, and puts it into bin/. Binstubs are a shortcut - or alternative - to always using bundle exec. This gives you a file that can be run directly, and one that will always run the correct gem version used by the application.

Instead of prefixing everything with bundle exec all the time, you can alternatively make sure you run any commands by invoking its binstub. They are not generated by default, though Rails comes with a bunch of predefined binstubs out of the box.

To generate a binstub for a ruby command you run regularly, for example rspec or maybe rubocop, you'd just generate the binstub once, then use that:

bash
# in your project's folder run:
> bundle binstubs rubocop

# this will create a script bin/rubocop,
# which you use instead of plain rubocop from now on
> bin/rubocop
create a binstub

And that's better than bundle exec rubocop?
Well, I'd say slightly? But not good enough, let's drop the bin/ too!

PATH to sanity

binstubs are always going to reside in a directory called bin inside the current projects directory. This allows me to add ./bin to my shell's PATH variable, which will allow the shell to find any binstubs that are there, without me having to tell it they are in the bin directory.

I'm using the fish shell, so I added this line to the bottom of my config.fish file to add bin in front of any other paths to look in for executables:

bash
set PATH ./bin $PATH
~/.config/fish/config.fish

This will work similarly if you are using zsh or bash.

Now, from a ruby project's directory, I can run rubocop, through its binstub, by just typing:

bash
> rubocop
ooh, that feels good

You can double-check that the command runs the right executable script using which:

bash
# check what executable is run
> which rubocop
./bin/rubocop

If the output of which looks different, then there's either something wrong with your PATH, the binstub for that particular ruby executable is missing or you might have forgotten to reload your shell after changing PATH?


What camp are you in? Do you not mind typing bundle exec, are you using a be alias, are you on team binstubs like me or none of the above?
Let me know! Drop me a message!