# Rails Pulse keeps performance data in your own database and lets an agent query it

> rails_pulse is an MIT licensed Rails engine that hooks into the instrumentation Rails already emits, writes request, query, job and exception data into tables you own, and mounts a dashboard, with a read-only JSON API, a CLI and an MCP server on top. It suits a Rails team that wants APM without sending data elsewhere, and it costs a migration, two scheduled jobs and a real decision about which requests you keep.

**railspulse/rails_pulse** — Rails Pulse is a comprehensive performance monitoring and debugging gem that provides insights into your Rails application's health.

- Repository: https://github.com/railspulse/rails_pulse
- Stars: 582 · Forks: 19
- Language: Ruby
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/railspulse-rails-pulse

## A Rails engine that reads the instrumentation Rails already emits

Rails Pulse is a comprehensive performance monitoring and debugging gem that provides insights into your Rails application's health, and the one-line summary is the pitch: performance monitoring that lives inside your Rails app, with slow requests, N+1 queries, background jobs and exceptions stored in your own database, no agent, no account, no data leaving your servers.

The mechanism is deliberately unremarkable. It is a Rails engine that hooks into the instrumentation Rails already emits, writes what it sees to a handful of tables, and mounts a dashboard that shows you where the time went.

The consequence of hooking into existing instrumentation is that there is nothing to instrument yourself and no agent to deploy. A request already carries its route, status, duration and a breakdown of database, view, cache, HTTP, mailer and Active Storage operations, and the engine records that rather than reimplementing measurement.

The requirements are stated up front: Ruby 3.2+, Rails 7.2+, tested on 7.2, 8.0 and 8.1, and SQLite, PostgreSQL or MySQL. The database choice matters more than it looks, because it decides whether the monitoring data lives in the same database as the application data or somewhere else.

The repository layout confirms the shape of the thing: an app/ directory for the engine's Rails application code, db/ for the migrations, exe/ for the executables that provide the CLI, lib/ for the gem's library code, and a package.json describing the asset build system for the dashboard's JavaScript.

## Four kinds of data in one place, and a request timeline

The feature list is where the scope of the tool becomes clear, and the first item is the one that shapes the dashboard.

Each request stores its route, status, duration and a timeline of the SQL, view, cache, HTTP, mailer and Active Storage operations inside it. The screenshots in the documentation show that as a request trace with action, view and database operations on a timeline, and a performance breakdown by database, view and application time.

Queries get the deepest treatment. SQL is normalised and fingerprinted, so you see execution counts and P95 per statement shape, N+1 patterns, an EXPLAIN plan and index suggestions. Fingerprinting is the key idea, since a query that runs ten thousand times in slightly different literal forms is one problem, not ten thousand, and per-shape P95 is what turns a slow page into a specific fix.

Jobs and exceptions are in the same place. For every Active Job class on any adapter you get duration, queue wait and failure rate. Unhandled exceptions from requests and jobs are grouped by class and location, with filtered params and backtraces.

The top of the screen is a health bar counting healthy, slow and critical routes, queries, jobs and exception groups, plus a ranked needs attention list telling you what to fix first. That single screen is the argument for the gem: a monitoring tool that makes you read its dashboard.

The one thing that is not here is a distributed trace. This is in-process instrumentation, so a request that leaves your Rails app for another service and comes back is not stitched together.

## Numbers over time, with deploys drawn on the chart

The metrics section is what turns the dashboard from a debugging aid into a regression detector.

There are hourly, daily, weekly and monthly summaries with P50, P95 and P99, your service level objectives drawn as lines on the charts, and a marker for every deploy so a regression lines up with the release that caused it.

The deploy markers are the feature worth dwelling on. Performance work goes wrong when a graph shows a line going up and nobody can say which commit did it. The documentation gives you two ways to record a deploy: a rails command, `rails rails_pulse:record_deployment[sha]`, run from a release script, or a POST to /rails_pulse/deployments with the API token from CI. Either way the chart draws a vertical line at that moment, and the route detail view shows a two week P95 and P99 chart with the objective lines and the deploy markers on it.

The service level objective lines are the other half. An SLO drawn as a line on the same axis as the percentile is a pass or fail you can see, rather than a number in a table you have to interpret. The thresholds for slow, very slow and critical, the objectives per percentile, what to ignore, what to tag and how long to keep are all configuration.

All of that is in config/initializers/rails_pulse.rb, with a configuration reference on the project site.

## The setup is a gem, a generator, a migration and two jobs

The quick start is short enough to copy, and the order matters.

```ruby
# Gemfile
gem "rails_pulse"
```

Then the install and the migration.

```bash
bundle install
rails generate rails_pulse:install
rails db:migrate
```

Then the engine gets mounted.

```ruby
# config/routes.rb
mount RailsPulse::Engine => "/rails_pulse"
```

And the two scheduled jobs, shown here with Solid Queue, an hourly summary and a daily cleanup.

```yaml
# config/recurring.yml
production:
  rails_pulse_summary:
    class: RailsPulse::SummaryJob
    schedule: "5 * * * *"
  rails_pulse_cleanup:
    class: RailsPulse::CleanupJob
    schedule: "0 1 * * *"
```

Open http://localhost:3000/rails_pulse and that is the whole setup.

The schedule is the part that needs a decision. The summary job at five past the hour rolls raw events into the hourly, daily, weekly and monthly summaries, and the cleanup job at one in the morning enforces the retention limits. If you are on a different queue adapter, the documentation says to use whatever your adapter provides, and there is a separate database and plain cron option in the installation guide for teams that do not want the jobs in the app's queue.

That last point is the one design decision to check first. If the monitoring data goes into your production database, the monitoring load is in your production database, and the cleanup job is what keeps the tables bounded.

## Built for production, which mostly means not blocking requests

The production claims in the feature list are specific and worth reading closely, because they describe failure behaviour rather than features.

Tracking is queued off the request thread and dropped rather than blocked under load. So when your app is already in trouble, the monitoring does not make it worse, and it does not queue an unbounded backlog of work either. It drops data. That is a deliberate trade of fidelity for the health of the application you are trying to diagnose.

If the gem is deployed before its migrations, tracking pauses and tells you what to run. That is a small detail with a real operational value, because the alternative is a stack trace on every request after a deploy that forgot the migration.

Retention is enforced by age and by row count so the tables never grow without bound. Two axes rather than one, which matters because a busy service can hit a row count limit in a day and a quiet one can hit an age limit in a year.

There is a separate deployment mode for the dashboard itself. `bundle exec rails_pulse_server` serves the UI from a separate process with its own health endpoint, so a slow report never competes with your app for a thread. For a service where the dashboard is slow because the underlying data is large, that is a real option rather than a workaround.

The documentation also covers authentication. The dashboard is authenticated by default outside development and test, and you point it at your own auth with a predicate, where anything but true is a 403.

## The MCP server is what makes this different from other APM gems

The last feature in the list is the one that distinguishes the project, and it is a bet on how debugging is going to work.

There is a read-only JSON API, a rails-pulse CLI and an MCP server, and they give Claude Code, Codex, Cursor or a CI script the same data: the slowest endpoints since the last deploy, the queries behind them, the jobs that failed. The documentation puts it plainly: your coding agent can read it, ask the agent why checkout got slow and it can go and look.

The CLI shows the shape of that.

```bash
rails-pulse configure          # URL and token, saved to ~/.rails-pulse
rails-pulse routes list --since 2026-06-01T00:00:00Z
rails-pulse install claude     # Claude Code skill: when and how to use the tools
```

The first command stores a URL and a token, the second queries routes since a timestamp, and the third installs a Claude Code skill that tells the agent when and how to use the tools. To use the MCP server you add the `mcp` gem to your Gemfile, a development group is enough, and register `rails-pulse mcp` as an MCP server.

The design choice to notice is that the agent path is read-only. It can query what happened, and it can be pointed at the slow endpoint and the query behind it, but it is not given a way to change the application. That is a sensible boundary for something that sits inside a production Rails process, and it is the reason the token is an API token and the configuration is a read predicate.

The project also ships a CI script story, and the documentation says the same data is available to a CI script, which is how you would wire a performance check into a pipeline rather than a dashboard.

## What a Ruby engineer gets, and what they have to bring

The last thing to weigh is the shape of the thing as a piece of Ruby.

The engine is a normal Rails engine with a normal migration, so the cost of adoption is a dependency, an initializer and a set of tables. The dashboard front end is a separate asset pipeline: the package.json in the repository describes a Node build with esbuild, postcss and cssnano for CSS, eslint for linting and vitest with jsdom for JavaScript tests, requiring Node 22.22.2 or newer. That is a normal modern setup for a Rails gem with a rich dashboard, and it is also a second toolchain to maintain if you fork the gem.

The repository also carries a .mise.toml for tool versions, a .rubocop.yml, a .simplecov file, an Appraisals file for testing against multiple dependency versions, a gemfiles/ directory for those Appraisal gemfiles, and a vitest.config.mjs alongside the Ruby test directory. The .env.example shows the development matrix, with DB set to sqlite3 by default and commented alternatives for postgresql and mysql2, plus separate credential variables for each, and a few advanced test switches for forcing database reconfiguration, using in-memory SQLite and limiting parallel test workers.

That last one is a hint about where the difficulty is. A monitoring tool that must work identically on SQLite, PostgreSQL and MySQL, and that stores time series, has to deal with the places those databases differ, and the option to limit parallel test workers exists because of that.

So the honest summary is that Rails Pulse is a well specified APM gem for a Rails team that wants its data at home, with an agent-facing surface that most APM gems do not have, and a dashboard that will add write traffic to a database you also have to operate.

## Conclusion

rails_pulse fits a Rails team that wants performance visibility stored in its own database, with no agent to install and no account to sign up for, and that is willing to run two scheduled jobs and accept a handful of tables in its production schema. It does not fit an application on Rails 7.1 or earlier, or on Ruby below 3.2, and it does not fit a team that wants APM semantics it does not implement, since the documentation describes request, query, job and exception tracking rather than distributed tracing across services. Before adopting it, decide where the tables live, because the documentation points at a separate database option and a plain cron setup as well as the in-app one, and read the authentication guide, since the dashboard falls back to HTTP Basic against environment variables unless you point it at your own auth.

## FAQ

### What does rails_pulse require to install?

Ruby 3.2 or newer, Rails 7.2 or newer tested on 7.2, 8.0 and 8.1, and SQLite, PostgreSQL or MySQL. Setup is the gem, the rails generate rails_pulse:install generator, the migration, and mounting the engine at /rails_pulse.

### How does rails_pulse track performance?

It is a Rails engine that hooks into the instrumentation Rails already emits, writes what it sees to a handful of tables, and mounts a dashboard. Tracking is queued off the request thread and dropped rather than blocked under load, and if the gem is deployed before its migrations, tracking pauses and tells you what to run.

### How do I give a coding agent access to rails_pulse data?

Set config.api_token, run rails-pulse configure to store the URL and token in ~/.rails-pulse, and register rails-pulse mcp as an MCP server after adding the mcp gem to your Gemfile. There is also a rails-pulse install claude command that installs a Claude Code skill describing when and how to use the tools.

### How is the rails_pulse dashboard protected?

The dashboard is authenticated by default outside development and test. You point it at your own auth with a predicate on config.authorize, where anything but true is a 403, and with nothing configured it falls back to HTTP Basic against the RAILS_PULSE_USERNAME and RAILS_PULSE_PASSWORD environment variables.

## Sources

- [Issues](https://github.com/railspulse/rails_pulse/issues)
- [License: MIT](https://github.com/railspulse/rails_pulse/blob/main/LICENSE)
- [railspulse/rails_pulse on GitHub](https://github.com/railspulse/rails_pulse)
- [README](https://github.com/railspulse/rails_pulse/blob/main/README.md)
- [Releases](https://github.com/railspulse/rails_pulse/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/railspulse-rails-pulse
