Rails Profiler adds a debug toolbar to every page of your app in development, keeps a profile of each request, and shows its SQL with N+1 detection and a flame graph of its timeline. Its MCP server lets your AI assistant read the same profiles.
Install itRuby 3.0 and Rails 7.0 or later, MIT license
GET /orders
app.myapp.test
200
240 ms
12 SQL queries
OrdersController#index240 ms
GET rates.myapp.test/v1/rates62 ms
pricing.apply_discounts30 ms
orders/index.html.erb124 ms
orders/_order.html.erb, 10 times90 ms
layouts/_footer.html.erb22 ms
N+1: SELECT customers, 10 times
0 ms60 ms120 ms180 ms240 ms
Controller
View
Partial
SQL
Cache
HTTP
Custom
Repeated query (N+1)
An illustration with invented data of the flame graph in the Timeline tab. Each bar is an event of a 240 ms GET /orders request, nested under the event that triggered it and sized by its duration. The controller reads the database and the cache, waits 62 ms on an outbound HTTP call to rates.myapp.test and runs a custom pricing.apply_discounts block. The view then renders a collection of 10 orders that loads each customer with its own query: the same SELECT runs 10 times, the N+1 pattern the profiler flags.
What it shows you
A toolbar on every page
A bar fixed to the bottom of every HTML page gives the request at a glance: status, duration, SQL queries and their time, views and partials, cache hit rate, outbound HTTP calls, logs, jobs and mails. Hover an item for the details, click it to open the matching tab of the full profile at /_profiler.
SQL, with N+1 detection
The Database tab lists every query with its time and model, bind values replaced by ?. When the same query runs three times or more, it is grouped and flagged as N+1, with the call stack that ran it. Read-only queries can be explained in place, inside a transaction that is always rolled back.
A flame graph of the request
The Timeline tab nests every instrumented event (controller, views, SQL, cache, HTTP) by time, so the slow part stands out. Wrap your own code in Profiler.measure to add it to the graph. Below it, function profiling shows the Ruby methods the request spent its time in, sampled with the optional stackprof gem or traced in full.
Profiles your AI assistant can read
The bundled MCP server gives Claude or any other MCP client 21 tools and 7 resources, over stdio or HTTP. Ask why a page is slow: the assistant finds the request, reads its queries and spots the N+1 itself, instead of you pasting SQL and logs into the conversation. From the root of your app, with Claude Code:
claude mcp add rails-profiler -- bundle exec rake profiler:mcp
Also recorded
Every request, job, console expression and test gets its own profile, opened tab by tab in the dashboard. Beyond SQL and the timeline, the profiler records:
Template and partial rendering times
Cache reads, writes, hits and misses
The fetch() and XMLHttpRequest calls of a page, Turbo Frames and Turbo Streams included
Outbound HTTP calls made with Net::HTTP, bodies included
Mails delivered or queued, with their body on demand
Rails logger output per request, filtered by level
Translation lookups and missing I18n keys
Sidekiq and ActiveJob jobs
Every expression evaluated in rails console
RSpec and Minitest tests, with a test runner in the dashboard
ENV changes from the dashboard, without a restart
Several applications reporting to one dashboard and one MCP server (cluster mode)
A Chrome DevTools extension adds a Rails Profiler panel that lists the profiled requests of the page you are browsing, and opens the profile of each one right in DevTools.
Install
Add the gem to your Gemfile, then run bundle install. The library is named profiler: without the require, Bundler loads nothing of the gem.
gem "rails-profiler", require: "profiler"
Mount the dashboard in config/routes.rb:
Rails.application.routes.draw do
mount Profiler::Engine, at: '/_profiler' if Rails.env.development?
end
Open any page of your app in development: the toolbar is at the bottom, and /_profiler lists every profile. For function profiling in its default mode, also add gem "stackprof", require: false.
Out of the box it runs in development and test only, lets in requests from your own machine only, and masks your filter_parameters before anything is stored.
What it costs: measured with the repository's benchmark script on Ruby 3.4.6, on a synthetic page, the profiler adds 5.3 to 5.8 ms per request with function profiling off.