“It’s slow” is a starting point.
Application performance monitoring (APM) shows what happened inside your Rails app. Pair it with uptime checks and resource metrics to understand both what users experience and where to investigate.
Spend less time guessing
Open a captured request to inspect its SQL, view rendering, errors, and timing. Repeated queries and expensive operations give you a concrete place to start.
Catch trouble between visits
Uptime checks keep testing your site when you are away. Connect alerts so an incident reaches the people who can respond.
Look beyond the web request
A fast controller can still enqueue a slow job. Inspect background work, queue latency, database connections, and host resources to understand the wider problem.
Check that your fix helped
Use deploy history and comparable request samples to see whether a change reduced duration, query count, or errors under similar traffic.
Start with one app and one important user journey. Collector availability, sampling limits, and advanced features depend on your plan.
From a Rails app to your first trace.
Create your account and application
Create an account, open Applications, and add your app. Copy its API key from the application settings.
Install the gem
Add this to your Rails app’s
Gemfile:gem "dead_bro", git: "https://github.com/rubydevro/dead_bro.git"Then install the dependency and generate the initializer:
bundle install bin/rails generate dead_bro:installConfigure the key and restart
Set
DEAD_BRO_API_KEYin your deployment’s environment or secret store. The generatedconfig/initializers/dead_bro.rbreads it like this:DeadBro.configure do |config| config.api_key = ENV["DEAD_BRO_API_KEY"] endDeploy and restart your web and worker processes so they load the gem and key. Use the same
DEAD_BRO_DEPLOY_ID, such as the release’s Git SHA, across replicas in a rollout to keep deploy history grouped correctly.Send traffic and verify the connection
Visit a real controller action in your app, then return to DeadBro and open the application’s captured requests. Look for the endpoint you just visited and inspect its timing and SQL. Run a normal background job to verify worker reporting too.
In application settings, review the sample rate and excluded controllers or jobs. Settings reach the gem through successful metric or heartbeat responses, so changes are not instantaneous.
Your first milestone: one request you recognize, in the expected environment, with useful timing data. Get this working before enabling additional collectors.
Full installation and configuration reference →Give each monitor a job.
Start with the signals you can act on. A healthy uptime check tells you your endpoint responds; request traces tell you whether the work behind it is slow.
Uptime: can users reach you?
Open uptime checks and create a GET check for your public site or health endpoint. Choose the frequency and add a string match when a specific response body matters.
Make it useful: check an endpoint that represents the service you care about. A basic Rails /up response verifies boot health; it does not prove checkout or every dependency works. Use tags to separate production and staging, and connect the check’s Slack integration.
Requests: is the app getting slower?
Use application performance views to find slow endpoints and open their request samples. Set an endpoint alarm threshold around an acceptable response time for that action.
Make it useful: prioritize important journeys such as sign-in and checkout. Tune notification modes and thresholds against normal traffic so alerts lead to a specific investigation.
Jobs: is background work keeping up?
Inspect captured ActiveJob executions for duration, failures, and SQL. Enable System monitor in application settings for supported queue metrics, including Sidekiq queue size and latency.
Make it useful: a growing queue with rising latency suggests work is arriving faster than workers finish it. Check slow or failed jobs and available workers before increasing concurrency.
Resources: what is under pressure?
In application settings, enable System monitor and the Database pool stats, Process stats, and System stats collectors you need. Open the application’s System monitor view.
Make it useful: compare CPU, memory, database pool usage, and queue pressure around the same time as a slowdown. Collection is best effort; some host metrics depend on the operating system, and rate metrics need more than one sample.
Close the alerting loop: connect the relevant app or uptime check to Slack, send a test notification, and agree who responds. Review noisy thresholds after you have seen normal traffic. Slack setup →
Follow the evidence to a fix.
Narrow the incident
Choose the app, environment, and time window. Start with the affected endpoint, error, or job. Check deploy history to see whether the change began around a release.
Open a representative request
Inspect a slow or failing sample and compare it with a healthy one for the same action. Look at total duration, SQL, rendering, and the exception backtrace when present. One unusual sample is a clue; repeated patterns make a stronger case.
Identify where the work goes
Repeated SQL?
Check N+1 flags and query call stacks. Look for association loading inside a loop, then test eager loading with
includesorpreload.One expensive query?
Inspect its SQL and call site. If EXPLAIN capture is enabled locally, use the captured plan to investigate scans and indexes. Plans contain estimates; validate changes with measured timings.
Rendering or memory?
Look for repeated partials, large result sets, or unnecessary object creation. Correlate request memory with process trends; one memory increase alone does not establish a leak.
An exception or failed job?
Read the message and backtrace, find the application frame, and inspect available request or job context. Reproduce the same input path in development.
Measure a small change locally
Use
DeadBro.analyzein a Rails console to inspect a block’s time, SQL, and memory without sending its analysis to DeadBro. For example, adapt this query to your own models:result = DeadBro.analyze("load users with profiles") do User.where(active: true).includes(:profile).limit(100).to_a end result.total_time_ms result.sql_count result.most_queries result.longest_queriesRun the original and changed versions with comparable data. The block executes normally, so use development or test data for operations that change records.
Deploy, then verify under real traffic
Compare the same endpoint and similar time windows before and after the release. Check duration, query count, and errors alongside traffic and sampling settings. Keep watching long enough to include the conditions that triggered the problem.
Not seeing what you expected?
No requests are appearing
Confirm the running web process has the correct API key, the gem is installed, and the process was restarted. Visit a controller action that is not excluded, check that tracking is enabled, and inspect application logs for authentication or network errors. A low sample rate can mean a single visit is not captured.
Requests appear, but jobs or system metrics do not
Restart workers with the same integration configuration. Verify the job uses a supported integration and is not excluded. For periodic metrics, enable System monitor and the relevant collectors in application settings, allow time for settings to reach the gem, and wait for subsequent samples. Unsupported metrics may be absent.
A slow query has no EXPLAIN plan
Plan capture requires local opt-in with config.explain_enabled = true, as well as permission in application settings and your plan. Requires dead_bro ≥ 0.2.33; on 0.2.32 or older, use config.explain_analyze_enabled = true instead. Only eligible read queries above the threshold are considered. Read the SQL analysis reference.
Alerts are too noisy
Review the endpoint threshold and notification mode, and exclude unhelpful health-check traffic from APM when appropriate. Keep uptime checks focused on meaningful availability signals. Send a test after changing Slack configuration to verify the destination.
Start with one useful answer.
Connect your app, capture a request, and add an uptime check. Then use the next slow request or error to work through your first investigation.