The Basic Rails Server
New Rails 7 apps come into two basic flavors: (1) By default, using Importmaps, and (2) with a Node-friendly setup like JSBundling or Shakapacker.
This has led to much confusion about how to start rails.
In the old, old days, many developers started rails using rails server
. That was a bad idea. Two alternatives were always better:
#1:
bundle exec rails server
#2:
bin/rails server
Starting using just rails server
was never a good idea, and the correct way to start rails was by using either bundle exec rails server
or bin/rails server
, which do the same thing.
The bin/rails script
lives inside of your Rails app, and you can look at it. Here is what it does:
#!/usr/bin/env ruby
APP_PATH = File.expand_path("../config/application", __dir__)
require_relative "../config/boot"
require "rails/commands"
Note that the first thing it does is set APP_PATH
, and then it requires the Rails boot file.
If you ran start rails
in the old days (the naïve way, without the bin/
and without bundle exec
), you were bypassing the Bundler setup of your application. Bypassing the Bundler setup of your app means that you’re not locking your dependencies to what’s in your Gemfile.lock. That’s a bad idea, because you may accidentally boot Gem versions installed globally from your Ruby instead of ensuring you have the exact Gem versions from your Gem lock file.
For this reason, the default way to start rails always involves either #1 or #2 above to make sure you’re running your code through Bundler to ensure you’re using the correct gem versions.
Foreman
Small Rails app that have no extra services (Node compiling, Sass watching, Redis, etc.). They work using the old techniques above, simply booting only the Rails server. You can upgrade to a Foreman app when you add extra services. This is how you would be set up if you started with JSBundling, Shakapacker, CSSBundling, and other setups too.
If you create a JSBundling or CSSBundling app, notice how to use Foreman to start all of your dev services at once for you in one terminal window.
Example your Procfile.dev
. It might look something like this:
web: bin/rails server -p 3000 js: yarn build --watch css: yarn build:css --watch
This is a simple Procfile.dev
that has 3 services: web, js, and css. The web service is what starts the Rails server, and the js and css services are “watchers” that watch for changes to your JS or CSS and recompile them on the fly.
For JSBundling Rails apps, you will start all services using either of two methods:
#1:
foreman start -f Procfile.dev "$@"
#2: use the shortcut you find inside of the bin/
folder:
bin/dev
This tells Rails to start all associated services at once in a single tab. Notice that bin/dev is just starting Foreman for you:
#!/usr/bin/env bash if ! command -v foreman &> /dev/null then echo "Installing foreman..." gem install foreman fi foreman start -f Procfile.dev "$@"
When starting on a Rails app that you haven’t worked with before, be sure to understand which kind of Rails app you are working with.
bin/dev
is useful even without cssbundling/jsbundling. For example, for Tailwind, so our bin/dev you would put the Tailwind watcher process into your Procfile.dev. A
And for Stripe, the bin/dev also runs the stripe webhook CLI. You can add anyting that needs a “background process,” like Sidekiq, Dart SASS, Redis, etc.
bin/dev
gives us a single command to start and stop it all via a Procfile. When you CTRL-C the window running Foreman, Foreman sends the “kill” command to all of the processes it started, shutting them all down at once.
Foreman pipes all the STDOUTs to 1 screen, which is bad for debugging.
The Debugging Problem When Using Foreman
When using Foreman, you have a severe problem if you want to debug. (ruby-debug, byebug, pry, etc). When you drop into a debugger by setting a debugger, Ruby does actually halt the execution and goes into the debugger. However, the output you see in your Terminal window is confusing, and when you try to interact with the debugger, the characters you type don’t appear on the screen as you would normally expect them to.
Take a look:
• The output in your Terminal window does not look like a standard debugger, which should halt and prompt you for interactive input.
• When you type interactive output, the characters appear at the bottom of the screen, and the result printed contains confusing information.
That’s because the debugger is in the same stream output as Foreman, so you lose the ability to have a clean interface for 1) seeing what you are typing, 2) hitting enter and seeing the result. It’s very messy and is not recommended for debugging.
For debugging only, you will need to fall back to the multi-window strategy.
A normal debugging interaction looks like this:
Alternatives to Foreman
Overmind is another process manager that is similar to Foreman but with some additional features. It provides better support for managing long-running processes, such as websockets, and also supports zero-downtime deploys.
Another alternative is Hivemind. The makers of Hivemind created it for Rails applications. It can automatically detect and manage your Rails processes, including your web server, worker processes, and other background jobs.
Regarding which one to choose for your project, it depends on your specific needs. Foreman is a good choice for a simple process manager that is widely used and well-supported.
If you have long-running processes that must be managed carefully, then Overmind might be a better fit. Hivemind is a good option if you are specifically working with a Rails application and want a process manager that is tailored to that use case.
• Introducing Overmind and Hivemind—Martian Chronicles, Evil Martians’ team blog
If you are using Overmind, the setup will modify the bin/dev
script to call Overmind instead of Foreman.
Then when you run bin/dev
, it runs overmind s -f Procfile.dev
.
Overmind uses tmux to manage the processes, which allows you to do overmind connect web
or overmind connect sidekiq
(or any of the other process names you defined).
This is helpful if you want a good debugging experience since Foreman doesn’t allow you to interact with the processes.
Here is a quick function to detect the kind of Rails app and start it appropriately.
startrails () { if [ -f bin/dev ] then bin/dev elif [ -f Procfile ] then bundle exec foreman start else bundle exec bin/rails server -p 3000 -b 0.0.0.0 | stdlog fi } alias sr=startrails