docs/oss/building-features/process-managers.md
React on Rails requires running multiple processes simultaneously during development:
React on Rails includes bin/dev which automatically uses Overmind or Foreman:
./bin/dev
This script will:
.dev-services.yml exists)precompile_hook once (if configured in config/shakapacker.yml)SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true to prevent duplicate executionIf you have configured a precompile_hook in config/shakapacker.yml, bin/dev will automatically:
SHAKAPACKER_SKIP_PRECOMPILE_HOOK environment variableNote: Shakapacker 9.4.0+ supports SHAKAPACKER_SKIP_PRECOMPILE_HOOK natively. For Shakapacker 9.0-9.3, script-based hooks remain reliable when the script includes a self-guard:
exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true"
bin/dev warns only when your hook cannot safely self-guard (for example, a direct command hook, or a script hook missing the guard line).
This eliminates the need for manual coordination in your Procfile.dev. For example:
Before (manual coordination with sleep hacks):
# Procfile.dev
wp-server: sleep 15 && bundle exec rake react_on_rails:locale && bin/shakapacker --watch
After (automatic coordination via bin/dev):
# Procfile.dev
wp-server: bin/shakapacker --watch
# config/shakapacker.yml
default: &default
precompile_hook: 'bundle exec rake react_on_rails:locale'
[!TIP] For HMR with SSR setups (two webpack processes), use a script-based hook instead of a direct command. Script-based hooks can include a self-guard that prevents duplicate execution regardless of Shakapacker version. See the i18n documentation for an example.
If your app currently uses a direct command hook, such as:
precompile_hook: 'bundle exec rake react_on_rails:locale'
migrate to a script-based hook:
Create bin/shakapacker-precompile-hook:
#!/usr/bin/env ruby
# frozen_string_literal: true
exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true"
system("bundle", "exec", "rake", "react_on_rails:locale", exception: true)
Make it executable:
chmod +x bin/shakapacker-precompile-hook
Update config/shakapacker.yml:
default: &default
precompile_hook: 'bin/shakapacker-precompile-hook'
This upgrade path works for both Shakapacker 9.0-9.3 and 9.4.0+.
See the i18n documentation for more details on configuring the precompile hook.
For projects with custom build requirements (ReScript, TypeScript compilation, multiple precompile tasks), consider handling precompile tasks directly in bin/dev instead of using the precompile_hook mechanism.
This approach provides:
See the Extensible Precompile Pattern guide for full details.
bin/dev can automatically verify that required external services (like Redis, PostgreSQL, Elasticsearch) are running before starting your development server. This prevents cryptic error messages and provides clear instructions on how to start missing services.
Create a .dev-services.yml file in your project root:
services:
redis:
check_command: 'redis-cli ping'
expected_output: 'PONG'
start_command: 'redis-server'
install_hint: 'brew install redis (macOS) or apt-get install redis-server (Linux)'
description: 'Redis (for caching and background jobs)'
postgresql:
check_command: 'pg_isready'
expected_output: 'accepting connections'
start_command: 'pg_ctl -D /usr/local/var/postgres start'
install_hint: 'brew install postgresql (macOS)'
description: 'PostgreSQL database'
A .dev-services.yml.example file with common service configurations is created when you run the React on Rails generator.
If .dev-services.yml exists, bin/dev will:
If .dev-services.yml doesn't exist, bin/dev works exactly as before (zero impact on existing installations).
When services are running:
š Checking required services (.dev-services.yml)...
ā redis - Redis (for caching and background jobs)
ā postgresql - PostgreSQL database
ā
All services are running
When services are missing:
š Checking required services (.dev-services.yml)...
ā redis - Redis (for caching and background jobs)
ā Some services are not running
Please start these services before running bin/dev:
redis
Redis (for caching and background jobs)
To start:
redis-server
Not installed? brew install redis (macOS) or apt-get install redis-server (Linux)
š” Tips:
⢠Start services manually, then run bin/dev again
⢠Or remove service from .dev-services.yml if not needed
⢠Or add service to Procfile.dev to start automatically
bin/dev automatically checks that your Rails database is accessible before starting the development server. This catches common issues like a missing database or a stopped database server, and provides clear error messages with specific commands to fix the problem.
When bin/dev starts, it runs a quick Rails runner process to verify:
If the database is not accessible, bin/dev prints a clear error message and exits before starting any processes.
Note: This check adds ~1-2 seconds to startup time as it spawns a Rails runner process.
There are three ways to disable the database check, listed by priority:
CLI flag (highest priority):
bin/dev --skip-database-check
Environment variable:
SKIP_DATABASE_CHECK=true bin/dev
Configuration in config/initializers/react_on_rails.rb:
ReactOnRails.configure do |config|
config.check_database_on_dev_start = false
end
When to disable:
ā ļø IMPORTANT: Commands in .dev-services.yml are executed during bin/dev startup without shell expansion for safety. However, you should still:
Recommended approach:
.dev-services.yml.example to version control (safe, documentation).dev-services.yml to .gitignore (developers copy from example)Execution order:
.dev-services.yml)config/shakapacker.yml)Overmind provides easier debugging and better signal handling:
# macOS
brew install overmind
# Linux
# See: https://github.com/DarthSim/overmind#installation
Foreman is a widely-used Ruby-based process manager:
# Install globally (NOT in Gemfile)
gem install foreman
Important: Do NOT add Foreman to your Gemfile. Install it globally on your system.
Why not in Gemfile?
From Foreman's documentation:
Foreman is not a library, and should not affect the dependency tree of your application.
Key reasons:
Install Foreman globally: gem install foreman
You can also run process managers directly instead of using bin/dev:
# With Overmind
overmind start -f Procfile.dev
# With Foreman
foreman start -f Procfile.dev
[!WARNING] When running Foreman directly (not via
bin/dev), Foreman injects its ownPORTenvironment variable (starting at 5000) into every subprocess. This causes${PORT:-3000}inProcfile.devto evaluate to Foreman's injected value rather than the fallback 3000.To avoid this, set
PORTexplicitly in your shell or.envfile before running Foreman:shPORT=3000 foreman start -f Procfile.dev # or add PORT=3000 to your .env file
bin/devhandles this automatically ā port detection runs before Foreman starts, soPORTis always set correctly when Foreman launches.
Edit Procfile.dev in your project root to customize which processes run and their configuration.
The default Procfile.dev includes:
rails: bundle exec rails s -p ${PORT:-3000}
dev-server: bin/shakapacker-dev-server
server-bundle: SERVER_BUNDLE_ONLY=true bin/shakapacker --watch
If you use git worktrees to work on multiple branches
in parallel, bin/dev automatically detects and avoids port conflicts ā no configuration needed.
When the default ports (3000 for Rails, 3035 for webpack-dev-server) are occupied, bin/dev
scans for the next free pair and prints:
Default ports in use. Using Rails :3001, webpack :3036
To override ports manually, create a .env file in the worktree (gitignored by default).
A .env.example is generated by rails g react_on_rails:install as a reference:
PORT=3001
SHAKAPACKER_DEV_SERVER_PORT=3036
When PORT or SHAKAPACKER_DEV_SERVER_PORT are set, auto-detection is skipped entirely.