Rails Error Dashboard
Rails-native error tracking for failure investigation — see the Ruby state and Rails runtime health behind every exception. Self-hosted, inside your app, in your own database. The gem is MIT and free forever.
gem "rails_error_dashboard"
bundle install
rails generate rails_error_dashboard:install
rails db:migrate
Open /red and raise a test exception. No monitoring account or ingestion service is required.
Try the live demo (gandalf / youshallnotpass) · Read the documentation · View on RubyGems
Beta: RED is functional and extensively tested, but configuration and APIs may change before 1.0. Supports Rails 7.0–8.1 and Ruby 3.2–4.0 (CI runs Ruby 3.2–3.4 against every supported Rails version; Ruby 4.0 is verified by the maintainer).
See the Ruby state and Rails runtime health behind every exception
Rails Error Dashboard (RED) is an open-source, self-hosted Rails engine for investigating production failures. It helps you answer not only what failed, but what was happening inside Ruby and Rails when it failed.
- Inspect local variables and the raising object's instance variables before the stack unwinds.
- See error-time Active Record, Puma, job queue, GC, memory and process health.
- Follow the SQL, cache, controller, job, mailer and other Rails events leading to the exception.
- Stay safe during error floods with progressive, count-preserving storm protection.
- Keep exception data on infrastructure you control.

The questions RED helps you answer
A stack trace tells you where execution stopped. RED helps you investigate the state behind it:
- What did
params, local variables and objects such as@ordercontain? - Was the Active Record pool exhausted?
- Was Puma out of thread capacity or building a backlog?
- Were jobs failing or queues growing?
- Was the process under GC, memory, descriptor or system pressure?
- Which SQL queries, cache operations or Rails events preceded the failure?
- Did a deploy introduce the error?
- Can the failing request become a cURL reproduction or RSpec regression-test scaffold?
What makes RED different
Failure-time Ruby state
Optionally capture local variables and — something no other error tracker does — the raising receiver's instance variables at TracePoint(:raise), with bounded serialization and your Rails filter_parameters applied to sensitive values. Binding objects are never retained.
Failure-time Rails health
Attach connection-pool, Puma, background-job, GC, memory, file-descriptor, TCP, RubyVM and YJIT state to the error record, refreshed on every captured occurrence — not merely to a separate periodic metrics chart. Every APM has these as time-series; none attaches them to the error. Opt-in; the procfs-backed fields are Linux-only.
Monitoring that degrades safely
During an error flood, RED progressively reduces captured context and database work, keeps a fresh exemplar every minute, records the storm in a Storm History ledger and reconciles exact in-process occurrence counts onto the error records. On by default.
Rails-specific investigation
Connect exceptions with SQL, caching, Active Job, Action Cable, Active Storage, Rack::Attack, deprecations and other Rails subsystems from one dashboard.
Things no other tracker does
Verified against Sentry, Honeybadger, AppSignal, Rollbar, Bugsnag, Airbrake, Raygun, New Relic, Datadog, Scout, Skylight and every self-hosted Rails tracker in August 2026 (the ledger):
- Copy as RSpec — a runnable request spec generated from the captured request (Sentry offers curl only).
- Swallowed-exception aggregate — raise-vs-rescue ratio per location, no APM span needed (Datadog's paid APM detects rescued exceptions but keeps no aggregate).
- Rack::Attack ledger — throttle, blocklist and track events persisted with per-rule stats and an AI-crawler classifier; rack-attack ships no UI of its own.
- Codeberg issue tracking, alongside GitHub, GitLab and Linear with two-way sync.
- The tracker instruments itself — its capture pipeline exported as OpenTelemetry spans, so you can audit its overhead in your own APM.
How RED compares
| Basic embedded tracker | General SaaS monitoring | RED |
|---|---|---|
| Stack trace and context | Cross-language telemetry and managed ingestion | Deep failure-time Ruby/Rails state inside the application boundary |
| Lightweight and local | Strong distributed and frontend observability | Rails-specific operational investigation and storm-safe local capture |
That makes RED a self-hosted Sentry alternative for teams that want Rails-specific depth and need error data to stay inside the application boundary — not a replacement for cross-language telemetry. RED has no mobile SDKs, no merge/split, no MCP server and no hosted operations.
Choose how you run it
- Store data in the application's existing PostgreSQL, MySQL/Trilogy or SQLite database.
- Isolate monitoring writes in a separate error database.
- Use synchronous writes, or async logging through Sidekiq or Solid Queue (GoodJob is detected for job-health stats but is not an async adapter).
- Track several Rails applications through a shared database.
No RED licence or event-ingestion fee, and no plan limits — your database is the only cap, and storm protection deliberately sheds context during floods.
Screenshots
Dashboard Overview — Live error stats, severity breakdown, and trend charts.

Error Detail — Full stack trace, cause chain, enriched context, and workflow management.

AI Help — Optional OpenAI or Anthropic assistance streamed directly inside the error detail page.

From the Community
All three [self-hosted alternatives] had an issue with error backtrace when using Turbo — RED did fix it… solid_errors and Faultline are not very active projects, RED is very active and @AnjanJ is very responsive in fixing issues. So, RED was my final choice.
— Gael Marziou (@gmarziou) · read the full discussion
Safety, performance and compatibility
- Host-app safety — nothing in the capture path raises into your app; every subscriber and callback is rescue-wrapped,
Thread.currentis cleaned up inensure, and the original exception is always re-raised. Variables, health and breadcrumbs are opt-in and off by default; storm protection is on by default and fails open. - Performance — the storm-protection hot path is a digest plus an atomic increment with no I/O; the figures quoted below are a maintainer's single-machine measurements and no benchmark script ships with the gem yet.
- Security — HTTP Basic Auth or your own
authenticate_withlambda (Devise, Warden, session); your Railsfilter_parametersare applied to params, variables and breadcrumbs; prompts are never recorded by LLM observability. Vulnerability reports: SECURITY.md. - Compatibility — Rails 7.0–8.1, Ruby 3.2–4.0, PostgreSQL, MySQL/Trilogy or SQLite;
turbo-railsplus ActionCable are needed for live updates (no polling fallback); the gem's own CSS/JS is inline but Bootstrap JS, Chart.js, highlight.js and Google Fonts load from CDNs, so it is not air-gap clean.
Features
Core (Always Enabled)
Error capture from controllers, jobs, and middleware. Custom-designed dashboard with dark/light mode, search, filtering, and real-time updates (the latter with turbo-rails + ActionCable in the host). Analytics with trend charts, severity breakdown, and spike detection. Workflow management with assignment, priority, snooze, mute/unmute (notification suppression), comments, and batch operations. Security via HTTP Basic Auth or custom lambda (Devise, Warden, session-based). Exception cause chains, enriched HTTP context, custom fingerprinting, CurrentAttributes integration, auto-reopen on recurrence, and sensitive data filtering — all built in.
Optional Features
Storm Protection — Circuit Breaker + Adaptive Sampling
When the error rate spikes (a bad deploy throwing thousands of errors a minute), the nightmare scenario for any in-process tracker is amplifying the outage with its own database writes. Storm protection is designed to shed the gem's own expensive work first — ON by default. The behaviour is measured (see Overhead below), though a bundled, reproducible benchmark is still to come.
- Per-fingerprint caps: past N occurrences/minute per error, context is shed, then rows are sampled deterministically (a fresh exemplar is always kept each minute)
- Global circuit breaker: sustained floods flip the gem to count-only mode — zero per-event I/O, exact in-memory counts reconciled onto error records every 30s. Async mode is gated too (a SolidQueue enqueue is itself a DB write)
- One storm notification replaces hundreds of per-error pings; auto-issue creation is capped (default 5 per 10 min) so a storm of new errors can't open 500 GitHub/Linear issues
- Honest accounting: a dashboard banner during/after the storm, a Storm History page with exact counts of everything shed, and
reached_open/peak-rate per episode. Counts are never extrapolated - Calm-weather economy: after 25 full-context captures of the same error per day, context is sampled (occurrence counting unaffected)
- Fails open: any internal storm-protection error means full capture. Protection can never be the thing that loses an error
config.enable_storm_protection = true # default
config.storm_open_threshold_per_second = 50 # per process
All thresholds are per process and individually configurable. Disable with one flag.
Overhead: the check is a digest plus an atomic increment; there is no I/O on the hot path. The maintainer's single-machine measurement (Apple Silicon, Ruby 4.0) was 2.4µs/error with protection active and calm, 2.95µs in count-only mode and 0.2µs when disabled, against a 5µs budget — a reproducible benchmark script is not yet part of the gem.
Breadcrumbs — Request Activity Trail
See exactly what happened before the crash — SQL queries, controller actions, cache operations, job executions, and mailer deliveries captured automatically via ActiveSupport::Notifications.
- Automatic capture — zero config beyond the enable flag
- N+1 query detection with aggregate patterns page
- Deprecation warnings with aggregate view (needs the host's deprecation behaviour to include
:notify; only requests that later raised are seen) - Custom breadcrumbs via
RailsErrorDashboard.add_breadcrumb("checkout started", { cart_id: 123 }) - Safe by design — fixed-size ring buffer, thread-local, every subscriber wrapped in rescue
config. = true
System Health Snapshot
Know your app's runtime state at the moment of failure — GC stats, process memory, thread count, connection pool utilization, Puma thread stats, RubyVM cache health, YJIT compilation stats, and deep runtime insights captured automatically.
- Sub-millisecond total snapshot, every metric individually rescue-wrapped
- No ObjectSpace scanning, no Thread backtraces, no subprocess calls
- RubyVM.stat: constant cache invalidations, shape cache stats
- YJIT runtime stats: compiled iseqs, invalidation count, code region sizes
- v0.5.2 — File descriptor utilization, system load averages, system memory pressure, TCP connection states, GC context (trigger reason, last major/minor), process swap and peak RSS — all with color-coded danger indicators
config.enable_system_health = true
N+1 Detection + Deprecation Warnings
Cross-error N+1 detection grouped by SQL fingerprint, and aggregate deprecation warnings with occurrence counts.


Requires breadcrumbs to be enabled. Deprecations are seen only when the host's ActiveSupport::Deprecation behaviour includes :notify (the production default does not) and only inside requests that later raised.
Operational Health Panels — Jobs, Database, Cache, ActionCable
Job Health — Aggregates the queue stats captured on each error (Sidekiq, SolidQueue or GoodJob auto-detected; needs enable_system_health). Not a live queue view — a per-error table with adapter badge, failed count (color-coded), sorted worst-first.

Database Health — PgHero-style live PostgreSQL stats (table sizes, unused indexes, dead tuples, vacuum timestamps) plus historical connection pool data from error snapshots. PostgreSQL-only for the system-table views; MySQL and SQLite show connection pool stats and hide the rest.

Cache Health — Per-error cache performance sorted worst-first.

ActionCable Health — Track WebSocket channel actions, transmissions, subscription confirmations, and rejections. Dashboard page at /errors/actioncable_health_summary with channel breakdown sorted by rejections. System health snapshot captures live connection count and adapter.
config.enable_actioncable_tracking = true # requires enable_breadcrumbs = true
ActiveStorage Health — Track file uploads, downloads, deletes, and existence checks across storage services (Disk, S3, GCS, Azure — any ActiveStorage backend). Dashboard page at /errors/activestorage_health_summary with per-service operation counts, average and slowest durations. Helps identify slow storage operations correlating with errors.
config.enable_activestorage_tracking = true # requires enable_breadcrumbs = true
LLM Observability — Calls, Tokens, Cost, Tool Use
Capture your app's LLM calls — through a Faraday middleware, OpenTelemetry GenAI spans or a manual notification; nothing is auto-instrumented — as breadcrumbs on the error that follows, with model, latency, token counts, estimated USD cost and tool-use requests. When a request crashes, you see the chat completion that preceded it: which model was called, how long it took, what it cost, and which tools it asked to invoke.
- Three capture paths — pick whichever matches your stack
- Cost estimated from a built-in pricing table (Claude 4.x, GPT-4o/o1, Gemini 2.5) — override per-model via
config.llm_pricing_overrides - Tool-call requests summarized inline; tool execution spans captured separately via the OTel path
- Prompts and completions are never recorded — only token counts and metadata (the
llm_observability_content_captureflag is reserved and currently a no-op) - Same host-app safety guarantees as the rest of the gem — never raises, never blocks the request, every callback rescue-wrapped
config. = true # required — LLM crumbs ride the breadcrumb pipeline
config.enable_llm_observability = true
# Optional — override the built-in pricing table for your account
# config.llm_pricing_overrides = { "claude-sonnet-4-6" => { input: 3.0, output: 15.0 } }
Path A — ruby-openai (Faraday middleware)
# Gemfile already has: gem "ruby-openai"
client = OpenAI::Client.new do |f|
f.use RailsErrorDashboard::Integrations::LlmMiddleware
end
Path B — ruby_llm (OpenTelemetry)
ruby_llm doesn't expose a Faraday hook, but the thoughtbot OTel instrumentation gem emits GenAI-semconv spans that our SpanProcessor picks up automatically.
# Gemfile
gem "ruby_llm"
gem "opentelemetry-sdk"
gem "opentelemetry-instrumentation-ruby_llm"
# config/initializers/opentelemetry.rb
OpenTelemetry::SDK.configure do |c|
c.use "OpenTelemetry::Instrumentation::RubyLLM"
end
The dashboard's LlmSpanProcessor registers itself with OpenTelemetry.tracer_provider during engine boot — no extra wiring.
Path C — anything else (Anthropic official SDK, Net::HTTP, gRPC, Ollama, …)
The official anthropic gem uses Net::HTTP directly (no Faraday hook), and many local-inference setups don't run OTel. Wrap any LLM call in ActiveSupport::Notifications.instrument — pass a mutable Hash so token counts can be filled in after the call:
payload = { provider: "anthropic", model: "claude-sonnet-4-6" }
ActiveSupport::Notifications.instrument("red.llm_call", payload) do
response = Anthropic::Client.new..create(
model: "claude-sonnet-4-6",
messages: [ { role: "user", content: "hi" } ]
)
payload[:input_tokens] = response.usage.input_tokens
payload[:output_tokens] = response.usage.output_tokens
end
# Tool execution — captured as its own llm_tool breadcrumb
ActiveSupport::Notifications.instrument("red.llm_tool_call",
tool_name: "search_database",
tool_arguments: { query: "..." }
) do
# run the tool
end
Payload contract matches the LlmCallEvent value object — see docs/LLM_OBSERVABILITY.md for the full field list.
Issue Tracking — GitHub, GitLab, Codeberg, Linear
One switch connects errors to your issue tracker. Platform becomes the source of truth — status, assignees, labels, and comments are mirrored live in the dashboard.
- Create & link: "Create Issue" button or paste an existing URL
- Auto-create: New errors auto-create issues. Critical/high severity always creates
- Lifecycle sync: Resolve → close, recur → reopen + comment, all via background jobs
- Platform mirror: Issue state, assignees (with avatars), labels (with colors), and comments displayed in the dashboard. Workflow controls (Resolve, Assign, Priority) replaced by platform state
- Two-way webhooks: Issue closed/reopened on platform syncs back to dashboard
- RED branding: Issues show "Created by RED (Rails Error Dashboard)"
config.enable_issue_tracking = true
config.issue_tracker_token = ENV["RED_BOT_TOKEN"]
# That's it — provider and repo auto-detected from git_repository_url
Linear works too — it's not a git forge, so set the provider and team key explicitly:
config.enable_issue_tracking = true
config.issue_tracker_provider = :linear
config.issue_tracker_repo = "ENG" # Linear team key (issues land as ENG-123)
config.issue_tracker_token = ENV["RED_BOT_TOKEN"] # lin_api_... personal API key
Closing maps to the team's first completed workflow state, reopening to unstarted/backlog. Two-way sync uses Linear webhooks (Linear-Signature HMAC verification).
User Impact Scoring
Dedicated /errors/user_impact page ranking errors by unique users affected — not occurrence count. An error hitting 1000 users once ranks higher than hitting 1 user 1000 times. Shows impact percentage (when total_users_for_impact is configured or auto-detected), severity badges, and per-error drill-down links.
No configuration needed — works automatically when errors have user_id (auto-detected via CurrentAttributes or current_user).
Scheduled Digests
Daily or weekly error summary emails — new errors, resolution rate, top errors by count, critical unresolved, and period-over-period comparison. HTML + text templates. Users schedule the job via SolidQueue, Sidekiq, or cron.
config.enable_scheduled_digests = true
config.digest_frequency = :daily # or :weekly
# config.digest_recipients = ["team@example.com"] # defaults to notification_email_recipients
Schedule: rails error_dashboard:send_digest PERIOD=daily
Release Tracking
Dedicated Releases page at /errors/releases shows a timeline of all deploys/versions with health stats. Answers: "Did this deploy introduce new errors?" and "Is this release stable?"
- Release timeline: Every version seen, sorted newest-first, with error counts, unique types, and time range
- "New in this release": Errors whose fingerprint first appeared in each version — flagged with a red badge
- Stability indicators: Green (at or below average), yellow (1-2x), red (>2x average error rate)
- Release comparison: Delta and percentage change vs the previous release
- Current release: Highlighted card with live health stats
- Zero config: Works automatically when
app_versionorgit_shais set (via config,APP_VERSION,GIT_SHA,HEROKU_SLUG_COMMIT, orRENDER_GIT_COMMITenv vars)
config.app_version = "1.2.0" # or set APP_VERSION env var
config.git_sha = ENV["GIT_SHA"] # auto-detected on Heroku/Render
config.git_repository_url = "https://github.com/user/repo" # enables SHA links
Source Code Integration + Git Blame
View actual source code directly in error backtraces with +/-7 lines of context. Git blame shows who last modified the code, when, and the commit message. Repository links jump to GitHub/GitLab/Bitbucket at the exact line.
config.enable_source_code_integration = true
config.enable_git_blame = true
Code Path Coverage (Diagnostic Mode)
Enable coverage via a dashboard button to see which production code paths were executed. Source code viewer overlays green checkmarks on executed lines and gray dots on unexecuted lines. Uses Ruby's Coverage.setup(oneshot_lines: true) — near-zero overhead, each line fires once. Zero overhead when off. Diagnostic mode only: coverage is process-global (a multi-threaded Puma blends requests), held in memory and not persisted. No error tracker integrates this; Coverband does it standalone with persistence.
config.enable_coverage_tracking = true # shows Enable/Disable buttons on error detail page
config.enable_source_code_integration = true # required for source code viewer
AI Help + Error Replay — Ask, Copy as cURL / RSpec / LLM Markdown
Replay failing requests with one click. Copy the request as a cURL command, generate an RSpec test, or copy all error details as clean Markdown for pasting into an LLM session. The LLM export includes app backtrace, cause chain, local/instance variables, breadcrumbs, environment, system health, and related errors — with framework frames filtered and sensitive data preserved as [FILTERED].
When an LLM provider is configured, the error detail page also shows an AI Help drawer. Users can ask follow-up questions about the current error and receive streamed Markdown answers from OpenAI or Anthropic without leaving the dashboard.
config.llm_provider = :openai # or :anthropic
config.llm_api_key = -> { Rails.application.credentials.dig(:openai, :api_key) }
config.llm_model = "gpt-5"
Privacy: AI Help sends the error's details (backtrace, context, and your question) to the configured provider (OpenAI or Anthropic). Keep
config.filter_sensitive_data = true(the default) so sensitive values are redacted as[FILTERED]before they leave your app.
Notifications — Slack, Discord, PagerDuty, Email, Webhooks
Multi-channel alerting with severity filters, per-error cooldown, milestone threshold alerts, and a per-environment allowlist (config.notification_environments = %w[production]) so a staging deploy never pages anyone.
config.enable_slack_notifications = true
config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL']
Environment Awareness — Filter, Badge, Notify per Environment
Every error records the environment it came from — production, staging, uat, preprod, any name your deploys use. The errors index filters by it, rows and the detail page carry a badge, the analytics page breaks errors down by environment, and every notification names it. The same error in staging and production is two rows with independent status, so resolving one never hides the other.
config.environment = ENV.fetch("ERROR_DASHBOARD_ENVIRONMENT", Rails.env) # free-form, defaults to Rails.env
config.notification_environments = %w[production] # nil = notify everywhere
Errors captured before v0.11.0 show no badge until they recur (the next occurrence claims the row) or you run rails rails_error_dashboard:backfill_environments.
Advanced Analytics

Seven analysis engines built in:
- Baseline Anomaly Alerts — Statistical spike detection (mean + std dev) with intelligent cooldown
- Fuzzy Error Matching — Jaccard similarity + Levenshtein distance to find related errors
- Co-occurring Errors — Detect errors that happen together within configurable time windows
- Error Cascade Detection — Identify potential cascades (A is followed by B is followed by C) with probability and delays — temporal association, not proven causation
- Error Correlation Analysis — Correlate errors with app versions, git commits, and users
- Platform Comparison — iOS vs Android vs API health metrics side-by-side
- Occurrence Pattern Detection — Cyclical patterns (business hours, weekends) and burst detection
Local Variable + Instance Variable Capture
See the exact values of local variables and instance variables at the moment an exception was raised — the most valuable debugging context possible.
- TracePoint(
:raise) captures locals and ivars before the stack unwinds - Configurable limits: max variable count, nesting depth, string truncation length
- Sensitive data auto-filtered via Rails
filter_parameters— passwords, tokens, and PII never stored - Never stores Binding objects — values extracted immediately, Binding is GC'd
- Independent config flags: enable one or both

config.enable_local_variables = true
config.enable_instance_variables = true
Swallowed Exception Detection
Detect exceptions that are raised but silently rescued — the hardest bugs to find. Only Datadog's paid APM detects rescued exceptions (Ruby 3.3+, and only inside a traced request); RED does it free, without an APM span, and aggregates the raise-vs-rescue ratio per location — no other tracker does that.
- Uses TracePoint(
:raise) + TracePoint(:rescue) to track exception lifecycle - Identifies code paths where exceptions are caught but never logged or re-raised
- Dashboard page at
/errors/swallowed_exceptionsshows detection counts, locations, and patterns - Memory-bounded aggregation with background flush
- Requires Ruby 3.3+

config.detect_swallowed_exceptions = true
On-Demand Diagnostic Dump
Snapshot your app's entire system state on demand — environment, GC stats, threads, connection pool, memory, job queue health, and more.
- Trigger via dashboard button or
rake rails_error_dashboard:diagnostic_dump - Dashboard page at
/errors/diagnostic_dumpswith full history - Useful for debugging intermittent production issues without reproducing them

config.enable_diagnostic_dump = true
Rack Attack Event Tracking
Track Rack Attack security events (throttles, blocklists, tracks) as breadcrumbs attached to errors, with a dedicated summary page.
- Captures throttle, blocklist, and track events automatically
- Dashboard page at
/errors/rack_attack_summarywith event breakdown and per-rule stats — rack-attack ships no UI of its own - Classifies AI-agent user agents (GPTBot, ClaudeBot, …) on
trackevents - Requires breadcrumbs to be enabled
config.enable_rack_attack_tracking = true
Process Crash Capture
Capture unhandled exceptions that crash the Ruby process via an at_exit hook — the last line of defense.
- Disk-based fallback: writes crash data to disk because the database may be unavailable during shutdown
- Imported automatically on next boot
- Captures exception details, backtrace, uptime, GC stats, thread count, and cause chain
- Honeybadger, Bugsnag and AppSignal have
at_exitreporters too; RED's writes to disk and imports at next boot because the database may already be gone during shutdown
config.enable_crash_capture = true
Plugin System
Event-driven extensibility with hooks for on_error_logged, on_error_resolved, on_threshold_exceeded. Built-in examples for Jira integration, metrics tracking, and audit logging.
class MyPlugin < RailsErrorDashboard::Plugin
def on_error_logged(error_log)
# Your custom logic
end
end
OpenTelemetry Export — Emit Gem Operations as Spans
Send the gem's error-capture pipeline as OpenTelemetry spans to your existing Datadog, Honeycomb, or Jaeger collector. Each stage of the capture path — DB write, breadcrumb harvest, system health snapshot, and notification dispatch — becomes a named child span so you can audit gem overhead from your own observability dashboards.
- Off by default — zero impact unless you opt in
- No-op when the OTel API gem isn't loaded
- Per-span-kind opt-in: enable only the stages you care about
- Every span individually rescue-wrapped — never raises into host code
- Boot-time warning if
enable_otel_export = truebutopentelemetry-apiisn't in the Gemfile
# Gemfile — only the API gem is required; the SDK is optional
gem "opentelemetry-api"
# config/initializers/rails_error_dashboard.rb
config.enable_otel_export = true
config.otel_service_name = "my-app" # falls back to application_name
config.otel_spans = [:capture, :breadcrumbs, :health, :notifications] # all (default)
# config.otel_spans = [:capture] # parent span only
Span names follow the rails_error_dashboard.<operation> convention, e.g. rails_error_dashboard.capture_error. Both attributes are attached to every span: rails_error_dashboard.version and rails_error_dashboard.service_name — use them to filter the gem's traffic in your dashboards.
Quick Start
1. Add to Gemfile
gem 'rails_error_dashboard'
2. Install with Interactive Setup
bundle install
rails generate rails_error_dashboard:install
rails db:migrate
The installer guides you through optional feature selection — notifications, performance optimizations, advanced analytics. All features are opt-in.
3. Visit your dashboard
http://localhost:3000/red
Default credentials: gandalf / youshallnotpass
Change these before production! Edit config/initializers/rails_error_dashboard.rb
4. Test it out
# In Rails console or any controller
raise "Test error from Rails Error Dashboard!"
Configuration
RailsErrorDashboard.configure do |config|
# Authentication
config.dashboard_username = ENV.fetch('ERROR_DASHBOARD_USER', 'gandalf')
config.dashboard_password = ENV.fetch('ERROR_DASHBOARD_PASSWORD', 'youshallnotpass')
# Or use your existing auth (Devise, Warden, etc.):
# config.authenticate_with = -> { warden.authenticated? }
# Optional features — enable as needed
config.enable_slack_notifications = true
config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL']
config.async_logging = true
config.async_adapter = :sidekiq # or :solid_queue, :async
end
Complete configuration guide →
Multi-App Support — Track errors from multiple Rails apps in a single shared database. Auto-detects app name, supports per-app filtering. Multi-App guide →
OpenTelemetry Export — Emit error-capture operations as OTel spans to Datadog, Honeycomb, or Jaeger. Add gem "opentelemetry-api" and set config.enable_otel_export = true. See OpenTelemetry Export above for full options.
Languages
RED ships in English with machine-translated previews for ten additional languages, covering the dashboard, its emails and its notification payloads. Native-speaking Rails developers are invited to review and improve them; once a locale has been reviewed it will be marked individually as community-reviewed. Eleven locales ship:
| Locale | Language | Status |
|---|---|---|
en |
English | Source language |
de |
Deutsch | Machine-translated, unreviewed |
es |
Español | Machine-translated, unreviewed |
fr |
Français | Machine-translated, unreviewed |
pt-BR |
Português (Brasil) | Machine-translated, unreviewed |
ja |
日本語 | Machine-translated, unreviewed |
ru |
Русский | Machine-translated, unreviewed |
uk |
Українська | Machine-translated, unreviewed |
pl |
Polski | Machine-translated, unreviewed |
it |
Italiano | Machine-translated, unreviewed |
zh-CN |
简体中文 | Machine-translated, unreviewed |
config.dashboard_locale = "de" # en, de, es, fr, pt-BR, ja, ru, uk, pl, it, zh-CN — default "en"
Users can also switch language per-session from the picker in the dashboard navbar, which overrides the configured default for them alone.
Everything but English is machine-translated and has not been reviewed by a native speaker. That is stated plainly rather than as "beta", which would imply a review process that has not happened — RED's maintainer reads only English. Key structure, interpolation variables and plural categories are verified mechanically in every locale; wording, register and idiom are not verified by anyone. A wrong or missing translation falls back to English, never to a broken page.
Corrections are very welcome, and a one-key PR is a perfectly good PR. If you read one of these languages, every locale has an open issue tracking its review — comment there, or report a bad translation without touching any code. You do not need to know Ruby, and you are not expected to review a whole file.
RED translates through its own private I18n backend, so it never reads, writes or mutates your application's I18n configuration — your locale and its available_locales are untouched.
Translations guide → — how the system works, how to correct a string, and how to add a locale.
FAQ
Does Rails Error Dashboard support a separate database for errors?
Yes. You can store errors in your app's existing database (shared) or in a dedicated, isolated database (separate). Set config.use_separate_database = true (or USE_SEPARATE_ERROR_DB=true) and point it at a separate connection — the engine routes all of its tables through connects_to, keeping error data fully isolated from your app data. Both modes are first-class and covered by the Database Options guide.
Which databases does it work with? SQLite, PostgreSQL, and MySQL/Trilogy — in either shared or separate-database mode.
Is this a self-hosted alternative to Sentry? Yes. It runs entirely inside your own Rails process — no external services, no SDK calling out, no per-event pricing. Error data never leaves your infrastructure.
Does it capture local variables like Sentry?
Yes — local and instance variables at the moment the exception is raised, via TracePoint(:raise), with sensitive-data filtering and configurable limits. It is opt-in. (Sentry's SDK can also capture locals as an opt-in option; RED adds instance variables and applies your Rails filter_parameters automatically.)
Will a flood of errors take down my app? No. Storm protection (a circuit breaker with adaptive sampling, ON by default) makes the gem degrade itself first during error floods — occurrence counts stay exact while it sheds the expensive work, and a Storm History page shows exactly what was shed. There is no I/O on the hot path — the check is a digest and an atomic increment.
Does it work with my background jobs?
Yes — errors raised in jobs are captured, and it can log errors asynchronously through Sidekiq or SolidQueue (or the in-process :async adapter). Sidekiq, SolidQueue and GoodJob are all auto-detected for the job-queue stats stored on each error.
Does it work with my authentication?
Yes — HTTP Basic Auth out of the box, or a custom authenticate_with lambda that integrates with Devise, Warden, or session-based auth.
Can it track more than one app? Yes — multi-app support tracks errors from multiple Rails apps in one dashboard with per-app filtering.
What Rails and Ruby versions are supported? Rails 7.0–8.1 and Ruby 3.2–4.0.
Documentation
Getting Started
- Quickstart Guide — 5-minute setup
- Configuration — All configuration options
- Uninstalling — Clean removal
Features
- Complete Feature List — Every feature explained
- Notifications — Multi-channel alerting
- Source Code Integration — Inline source + git blame
- Batch Operations — Bulk resolve/delete
- Real-Time Updates — Live dashboard
- Error Trends — Charts and analytics
- Translations — Eleven shipped locales, correcting a string, adding a language
Advanced
- Multi-App Support — Track multiple applications
- Plugin System — Build custom integrations
- API Reference — Complete API documentation
- Customization — Customize everything
- Database Options — Separate database setup
- Database Optimization — Performance tuning
- Mobile App Integration — log mobile-originated errors through your own API endpoint, tagged by platform
- FAQ — Common questions answered
Architecture
Built with CQRS (Command/Query Responsibility Segregation):
- Commands — LogError, ResolveError, BatchOperations (writes)
- Queries — ErrorsList, DashboardStats, Analytics (reads)
- Services — PlatformDetector, SimilarityCalculator (business logic)
- Plugins — Event-driven extensibility
Testing
An RSpec suite of unit, request and browser-based system specs runs in CI on every supported Rails version (see the Tests badge above); the current count lives in the CI log rather than here, where it would go stale.
bundle exec rspec # Full suite
bundle exec rspec spec/system/ # System tests (Capybara + Cuprite)
HEADLESS=false bundle exec rspec spec/system/ # Visible browser
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Write tests, ensure all pass (
bundle exec rspec) - Commit and push
- Open a Pull Request
git clone https://github.com/AnjanJ/rails_error_dashboard.git
cd rails_error_dashboard
bin/setup # Installs deps, hooks, runs tests
Development guide → · Testing guide →
License
Available as open source under the MIT License.
Acknowledgments
Built with Rails · Custom design tokens with Bootstrap 5 JS for tooltips and modals · Charts by Chart.js · Pagination by Pagy · Docs theme by Jekyll VitePress Theme by @crmne
Contributors
Special thanks to @bonniesimon, @gundestrup, @midwire, @RafaelTurtle, @j4rs, @gmarziou, and @antarr. See CONTRIBUTORS.md for the full list.
Support
If this gem saves you some headaches (or some money on error tracking SaaS), consider sponsoring the project. It keeps RED going and lets me know people are finding it useful.
Made with ❤️ by Anjan
One Gem to rule them all, One Gem to find them, One Gem to bring them all, and in the dashboard bind them.