Introduction
Performance optimization is critical for delivering fast, responsive Rails applications. This comprehensive guide covers the most important profiling tools you should implement in your Rails 8 application, complete with setup instructions and practical examples.
Why Profiling Matters
Before diving into tools, let’s understand why profiling is essential:
- Identify bottlenecks: Pinpoint exactly which parts of your application are slowing things down
- Optimize resource usage: Reduce memory consumption and CPU usage
- Improve user experience: Faster response times lead to happier users
- Reduce infrastructure costs: Efficient applications require fewer server resources
Essential Profiling Tools for Rails 8
1. Rack MiniProfiler
What it does: Provides real-time profiling of your application’s performance directly in your browser.
Why it’s important: It’s the quickest way to see performance metrics without leaving your development environment.
Installation:
# Gemfile
gem 'rack-mini-profiler', group: :development
Usage example:
After installation, it automatically appears in your browser’s corner showing:
- SQL query times
- Ruby execution time
- Memory allocation
- Flamegraphs (with additional setup)
Advantages:
- No configuration needed for basic setup
- Shows N+1 query warnings
- Integrates with Rails out of the box
GitHub: https://github.com/MiniProfiler/rack-mini-profiler
Documentation: https://miniprofiler.com/
2. Bullet
What it does: Detects N+1 queries, unused eager loading, and missing counter caches.
Why it’s important: N+1 queries are among the most common performance issues in Rails applications.
Installation:
# Gemfile
gem 'bullet', group: :development
Configuration:
# config/environments/development.rb
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
end
Example output:
GET /posts
USE eager loading detected
Post => [:comments]
Add to your query: Post.includes([:comments])
Advantages:
- Catches common ORM performance issues early
- Provides specific recommendations for fixes
- Works across all environments
GitHub: https://github.com/flyerhzm/bullet
Documentation: https://github.com/flyerhzm/bullet/blob/master/README.md
3. Ruby Prof (and StackProf)
What it does: Low-level Ruby code profiler that shows exactly where time is being spent.
Why it’s important: When you need deep insight into method-level performance characteristics.
Installation:
# Gemfile
gem 'ruby-prof', group: :development
gem 'stackprof', group: :development
Usage example:
# In your controller or service object
result = RubyProf.profile do
# Code you want to profile
end
printer = RubyProf::GraphPrinter.new(result)
printer.print(STDOUT, {})
For StackProf:
StackProf.run(mode: :cpu, out: 'tmp/stackprof.dump') do
# Code to profile
end
Advantages:
- Method-level granularity
- Multiple output formats (call graphs, flamegraphs)
- StackProf is sampling-based so has lower overhead
GitHub: https://github.com/ruby-prof/ruby-prof
Documentation: https://github.com/ruby-prof/ruby-prof/blob/master/README.md
StackProf Alternative:
GitHub: https://github.com/tmm1/stackprof
Documentation: https://github.com/tmm1/stackprof/blob/master/README.md
4. Memory Profiler
What it does: Tracks memory allocations and helps identify memory bloat.
Why it’s important: Memory issues can lead to slow performance and even crashes.
Installation:
# Gemfile
gem 'memory_profiler', group: :development
Usage example:
report = MemoryProfiler.report do
# Code to profile
end
report.pretty_print(to_file: 'memory_report.txt')
Advantages:
- Shows allocated objects by class and location
- Tracks retained memory after GC
- Helps find memory leaks
GitHub: https://github.com/SamSaffron/memory_profiler
Documentation: https://github.com/SamSaffron/memory_profiler/blob/master/README.md
5. Skylight
What it does: Production-grade application performance monitoring (APM).
Why it’s important: Understanding real-world performance characteristics is different from development profiling.
Installation:
# Gemfile
gem 'skylight'
Configuration:
# config/skylight.yml
production:
authentication: [YOUR_AUTH_TOKEN]
Advantages:
- Low-overhead production profiling
- Endpoint-level performance breakdowns
- Database query analysis
- Exception tracking
Website: https://www.skylight.io
Documentation: https://docs.skylight.io
GitHub: https://github.com/skylightio/skylight-ruby
6. AppSignal
What it does: Full-stack performance monitoring and error tracking.
Why it’s important: Provides comprehensive insights across your entire application stack.
Installation:
# Gemfile
gem 'appsignal'
Then run:
bundle exec appsignal install YOUR_PUSH_API_KEY
Advantages:
- Error tracking alongside performance
- Host metrics integration
- Background job monitoring
- Magic Dashboard for quick insights
Website: https://appsignal.com
Documentation: https://docs.appsignal.com/ruby
GitHub: https://github.com/appsignal/appsignal-ruby
7. Derailed Benchmarks
What it does: Suite of benchmarks and performance tests for your application.
Why it’s important: Helps catch performance regressions before they hit production.
Installation:
# Gemfile
group :development, :test do
gem 'derailed_benchmarks'
end
Usage examples:
# Memory usage at boot
bundle exec derailed bundle:mem
# Performance per route
bundle exec derailed exec perf:routes
Advantages:
- CI-friendly performance testing
- Memory usage analysis
- Route-based performance testing
GitHub: https://github.com/schneems/derailed_benchmarks
Documentation: https://github.com/schneems/derailed_benchmarks/blob/master/README.md
8. Flamegraph Generation
What it does: Visual representation of where time is being spent in your application.
Why it’s important: Provides an intuitive way to understand call stacks and hot paths.
Installation:
# Gemfile
gem 'flamegraph'
gem 'stackprof' # if not already installed
Usage example:
Flamegraph.generate('flamegraph.html') do
# Code to profile
end
Advantages:
- Visual representation of performance
- Easy to spot hot paths
- Interactive exploration
GitHub: https://github.com/SamSaffron/flamegraph
Documentation: http://samsaffron.github.io/flamegraph/rails-startup.html
Additional Helpful Tools 🔧
9. Benchmark-ips
Benchmark-ips (iterations per second) is a superior benchmarking tool compared to Ruby’s standard Benchmark library. It provides:
- Iterations-per-second measurement – More intuitive than raw time measurements
- Statistical analysis – Shows standard deviation between runs
- Comparison mode – Easily compare different implementations
- Warmup phase – Accounts for JIT and cache warming effects
Benchmark-ips solves these problems and is particularly valuable for:
- Comparing algorithm implementations
- Testing performance optimizations
- Benchmarking gem alternatives
- Validating performance-critical code
GitHub: https://github.com/evanphx/benchmark-ips
Documentation: https://github.com/evanphx/benchmark-ips/blob/master/README.md
Installation
# Gemfile
gem 'benchmark-ips', group: :development
Basic Usage:
require 'benchmark/ips'
Benchmark.ips do |x|
x.report("addition") { 1 + 2 }
x.report("addition with to_s") { (1 + 2).to_s }
x.compare!
end
Advanced Features:
Benchmark.ips do |x|
x.time = 5 # Run each benchmark for 5 seconds
x.warmup = 2 # Warmup time of 2 seconds
x.report("Array#each") { [1,2,3].each { |i| i * i } }
x.report("Array#map") { [1,2,3].map { |i| i * i } }
# Add custom statistics
x.config(stats: :bootstrap, confidence: 95)
x.compare!
end
# Memory measurement
require 'benchmark/memory'
Benchmark.memory do |x|
x.report("method1") { ... }
x.report("method2") { ... }
x.compare!
end
# Disable GC for more consistent results
Benchmark.ips do |x|
x.config(time: 5, warmup: 2, suite: GCSuite.new)
end
Sample Output:
Warming up --------------------------------------
addition 281.899k i/100ms
addition with to_s 261.831k i/100ms
Calculating -------------------------------------
addition 8.614M (± 1.2%) i/s - 43.214M in 5.015800s
addition with to_s 7.017M (± 1.8%) i/s - 35.347M in 5.038446s
Comparison:
addition: 8613594.0 i/s
addition with to_s: 7016953.3 i/s - 1.23x slower
Key Advantages
- Accurate comparisons with statistical significance
- Warmup phase eliminates JIT/caching distortions
- Memory measurements available through extensions
- Customizable reporting with various statistics options
10. Rails Performance (Dashboard)
What is Rails Performance?
Rails Performance is a self-hosted alternative to New Relic/Skylight that provides:
- Request performance tracking
- Background job monitoring
- Slowest endpoints identification
- Error tracking
- Custom event monitoring
Why It’s Important
For teams that:
- Can’t use commercial SaaS solutions
- Need to keep performance data in-house
- Want historical performance tracking
- Need simple setup without complex infrastructure
GitHub: https://github.com/igorkasyanchuk/rails_performance
Documentation: https://github.com/igorkasyanchuk/rails_performance/blob/master/README.md
Installation
# Gemfile
gem 'rails_performance', group: :development
Then run:
rails g rails_performance:install
rake db:migrate
Configuration
# config/initializers/rails_performance.rb
RailsPerformance.setup do |config|
config.redis = Redis.new # optional, will use Rails.cache otherwise
config.duration = 4.hours # store requests for 4 hours
config.enabled = Rails.env.production?
config.http_basic_authentication_enabled = true
config.http_basic_authentication_user_name = 'admin'
config.http_basic_authentication_password = 'password'
end
Accessing the Dashboard:
After installation, access the dashboard at:
http://localhost:3000/rails/performance
Custom Tracking:
# Track custom events
RailsPerformance.trace("custom_event", tags: { type: "import" }) do
# Your code here
end
# Track background jobs
class MyJob < ApplicationJob
around_perform do |job, block|
RailsPerformance.trace(job.class.name, tags: job.arguments) do
block.call
end
end
end
# Add custom fields to requests
RailsPerformance.attach_extra_payload do |payload|
payload[:user_id] = current_user.id if current_user
end
# Track slow queries
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
if event.duration > 100 # ms
RailsPerformance.trace("slow_query", payload: {
sql: event.payload[:sql],
duration: event.duration
})
end
end
Sample Dashboard Views:
- Requests Overview:
- Average response time
- Requests per minute
- Slowest actions
- Detailed Request View:
- SQL queries breakdown
- View rendering time
- Memory allocation
- Background Jobs:
- Job execution time
- Failures
- Queue times
Key Advantages
- Self-hosted solution – No data leaves your infrastructure
- Simple setup – No complex dependencies
- Historical data – Track performance over time
- Custom events – Track any application events
- Background jobs – Full visibility into async processes
Implementing a Complete Profiling Strategy
For a comprehensive approach, combine these tools at different stages:
- Development:
- Rack MiniProfiler (always on)
- Bullet (catch N+1s early)
- RubyProf/StackProf (for deep dives)
- CI Pipeline:
- Derailed Benchmarks
- Memory tests
- Production:
- Skylight or AppSignal
- Error tracking with performance context
Sample Rails 8 Configuration
Here’s how to set up a complete profiling environment in a new Rails 8 app:
# Gemfile
# Development profiling
group :development do
# Basic profiling
gem 'rack-mini-profiler'
gem 'bullet'
# Deep profiling
gem 'ruby-prof'
gem 'stackprof'
gem 'memory_profiler'
gem 'flamegraph'
# Benchmarking
gem 'derailed_benchmarks', require: false
gem 'benchmark-ips'
# Dashboard
gem 'rails_performance'
end
# Production monitoring (choose one)
group :production do
gem 'skylight'
# or
gem 'appsignal'
# or
gem 'newrelic_rpm' # Alternative option
end
Then create an initializer for development profiling:
# config/initializers/profiling.rb
if Rails.env.development?
require 'rack-mini-profiler'
Rack::MiniProfilerRails.initialize!(Rails.application)
Rails.application.config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.rails_logger = true
end
end
Conclusion
Profiling your Rails 8 application shouldn’t be an afterthought. By implementing these tools throughout your development lifecycle, you’ll catch performance issues early, maintain a fast application, and provide better user experiences.
Remember:
- Use development tools like MiniProfiler and Bullet daily
- Run deeper profiles with RubyProf before optimization work
- Monitor production with Skylight or AppSignal
- Establish performance benchmarks with Derailed
With this toolkit, you’ll be well-equipped to build and maintain high-performance Rails 8 applications.
Enjoy Rails! 🚀
