How the Rails Procfile Works, Foreman, Overmind, Hivemind

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:

When using Foreman (./bin/dev) the debugging experience is significantly sub-par and practically unusable.

• 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