RailsNexus
The extensible control plane for Rails applications
RailsNexus is an extensible operations and administration console for Rails applications. It provides everything you need to monitor, debug, and manage your Rails app in production — all from a single dashboard.
One gem. All your ops.
Features
🚨 Error Monitoring
- Exception Dashboard — Dark/light theme, keyboard shortcuts, tabbed detail view
- Cause Chains — Track root cause through chained exceptions
- Breadcrumbs — Activity trail leading up to each error
- Storm Protection — Circuit breaker for error floods
- User Impact Ranking — See which errors affect the most users
- Platform Detection — iOS, Android, Web, and API with automatic categorization
📊 Real-time Analytics
- Error Trends — Hourly, daily, and weekly exception patterns
- Platform Health — Per-platform error rates and response times
- Correlation Insights — Time-based, controller-based, and user-based correlations
- Baseline Monitoring — Detect anomalies against historical averages
- Occurrence Patterns — Cyclical and burst detection
- N+1 Query Detection — Automatic N+1 pattern identification
⏰ Cron Job Monitoring
- Job Tracking — Track scheduled job runs, failures, and history
- Success Rates — Per-job and overall success metrics
- Execution Times — Monitor job duration and detect slow jobs
- Cleanup — Automatic old job record removal
🖥️ Server Statistics
- Memory Usage — RAM and swap monitoring
- CPU Metrics — Load average and processor count
- Ruby/Rails Info — Versions and runtime details
- Process Info — Puma workers, thread counts
- Sidekiq Stats — Queue sizes, workers, processed/failed
🗄️ Database Health
- Connection Pool — Live pool status and utilization
- Table Statistics — Row counts, sizes, and growth
- Index Usage — Index hit rates and missing indexes
- Slow Queries — N+1 and slow query detection
🔄 Workflow Management
- Assignment — Assign exceptions to team members
- Priority — Set critical/high/medium/low priority levels
- Snooze — Temporarily silence exceptions (1h, 4h, 1d, 1w)
- Mute — Permanently silence resolved exceptions
- Comments — Add notes and status changes
🔍 Source Code Integration
- Inline Source — View source code directly in backtraces
- Git Blame — See who wrote each line and when
- On-demand Loading — AJAX-powered source fetching
- Security — Path traversal protection, read-only access
🔔 Notifications
- Webhooks — POST to Slack, Discord, or custom endpoints
- Telegram — Built-in notifier with rate limiting
- Sidekiq Middleware — Automatic background job exception logging
🛠️ Developer Experience
- Keyboard Shortcuts — Full keyboard navigation (
/,j,k,Enter,?) - Dark/Light Theme — Auto-detect or manual toggle, remembers preference
- Ransack Search — Advanced search and filtering
- RSS Feed — Subscribe to exception updates
- Cleanup/R retention — Auto-delete old exceptions
- Structured Logging — JSON log output with request context
- Tailwind-compatible — Works with or without Tailwind CSS
Requirements
- Ruby 3.2 or newer
- Rails 8.0 or newer
- SQLite 3, PostgreSQL, or MySQL/MariaDB
Installation
Add RailsNexus to your application:
# Gemfile
gem "rails_nexus"
Run the install generator:
bundle install
bin/rails generate rails_nexus:install
bin/rails db:migrate
This will:
- Copy the database migration with proper indexes.
- Create a configuration initializer at
config/initializers/rails_nexus.rb. - Mount the engine in your routes.
- Add
rescue_from StandardError, with: :log_exception_handlertoApplicationController.
The dashboard is now available at /rails_nexus.
Backup management is provided separately by backup_nexus. Add it when your application needs the backup dashboard and runner:
gem "backup_nexus"
Then run bin/rails generate backup_nexus:install.
Optional UI customization
The mounted dashboard already includes its CSS, Turbo, and Stimulus controllers. To copy the UI into your host application for customization, run:
bin/rails generate rails_nexus:customize
This generates:
- Self-contained layout with sidebar, topbar, and theme toggle
- Tabbed detail view with metadata grid
- Keyboard shortcuts dialog
- Stimulus controllers for theme, sidebar, and interactions
Options: --layout-only, --views-only, --stimulus-only, --initializer-only
Start logging exceptions
Include RailsNexus::ExceptionLoggable in your application controller:
class ApplicationController < ActionController::Base
include RailsNexus::ExceptionLoggable
end
RailsNexus logs the exception and then re-raises it so Rails keeps its normal error handling.
API controllers
For ActionController::API subclasses (e.g., API namespaces):
class Api::V1::BaseController < ActionController::API
include RailsNexus::ExceptionLoggable
end
Older applications using RailsOps::ExceptionLoggable remain supported for
boot compatibility. Update them to RailsNexus::ExceptionLoggable when
convenient.
Background jobs (Sidekiq)
RailsNexus automatically logs Sidekiq job exceptions when the middleware is configured:
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add RailsNexusSidekiqMiddleware
end
end
Protect the dashboard
The dashboard contains sensitive information. Do not expose it to unauthenticated users.
Authentication is applied globally by RailsNexus::ApplicationController,
including workflow mutations and source-code viewing. If auth_block is
missing, returns false/nil, or raises an error, every dashboard request
returns 403 Forbidden. Configure authentication:
# config/initializers/rails_nexus.rb
Rails.application.config.to_prepare do
RailsNexus.configure do |config|
config.auth_block = lambda do |controller|
controller.current_user&.admin?
end
end
end
Configuration
# config/initializers/rails_nexus.rb
Rails.application.config.to_prepare do
RailsNexus.configure do |config|
# ─── General ─────────────────────────────────────────────
config.application_name = "My App" # Dashboard title
config.per_page = 50 # Items per page (default: 30)
# ─── Authentication ──────────────────────────────────────
config.auth_block = lambda do |controller|
controller.current_user&.admin?
end
# ─── Exception Data ──────────────────────────────────────
config.exception_data = lambda do |controller|
{
user_id: controller.current_user&.id,
request_id: controller.request.request_id,
environment: Rails.env
}
end
# ─── Notifications ───────────────────────────────────────
config.after_create = lambda do |exception|
MyNotifier.alert(exception)
end
config.webhooks = ["https://hooks.slack.com/services/xxx"]
config.webhook_timeout = 5
config.webhook_headers = { "Authorization" => "Bearer token" }
# ─── Cleanup ─────────────────────────────────────────────
config.retention_days = 90 # Auto-delete after N days
# ─── Appearance ──────────────────────────────────────────
config.theme = "auto" # "light", "dark", or "auto"
config.body_class = "" # Custom body CSS class
config.use_host_layout = false # Use host app's layout
# ─── Advanced UI ─────────────────────────────────────────
config.show_stats = true # Stats overview cards
config.keyboard_shortcuts = true # Keyboard shortcuts
config. = true # Prev/next in detail view
config.backtrace_limit = 30 # Frames before collapse
config. = true # Metadata grid
config.show_environment = false # Environment tab
config.show_request = true # Request params tab
config. = [25, 50, 100] # Page size selector
# ─── Sidebar Links ───────────────────────────────────────
config. = [
{ label: "GitHub", url: "https://github.com/myorg/myapp" },
{ label: "Docs", url: "/docs" }
]
# ─── Logging ─────────────────────────────────────────────
config.logging_enabled = true
config.log_level = :info # :debug, :info, :warn, :error, :fatal
config.log_file = "log/rails_nexus.log" # JSON log file (nil = none)
config.log_backtrace = true
config.log_params = true
config.log_user_info = true
config.log_backtrace_limit = 20
config.deduplication_enabled = false
config.deduplication_window = 300 # Seconds
end
end
Keyboard shortcuts
Press ? in the dashboard to see all shortcuts:
| Key | Action |
|---|---|
/ |
Focus search |
j or ↓ |
Navigate down |
k or ↑ |
Navigate up |
Enter |
Open selected exception |
p or ← |
Previous exception |
n or → |
Next exception |
t |
Toggle dark/light theme |
1-5 |
Switch tabs |
Esc |
Close panel / Clear |
? |
Show shortcuts dialog |
Search & Filter
- Exception Type — Select dropdown, auto-submits on change
- Source — Exact controller/action filter
- Platform and Priority — Narrow errors by client and workflow priority
- Status and Assignee — Show active, muted, snoozed, or unassigned errors
- Occurrences — Focus on repeatedly occurring fingerprints
- Time Range — Today, 3 days, 7 days, 30 days
- Search — Search message, exception class, controller, and action with a 400ms debounce
- Sortable Columns — Sort by exception, platform, source, message, count, workflow, or last seen
- Active Filters — Pills with one-click dismiss
RailsNexus uses Ransack for filtering and sorting and Pagy for bounded, configurable pagination. Filter, sort, page-size, and page parameters are preserved across Turbo Frame updates.
Notifications
Webhooks
config.webhooks = [
"https://hooks.slack.com/services/T00/B00/xxx",
"https://discord.com/api/webhooks/xxx"
]
config.webhook_timeout = 5
config.webhook_headers = { "Authorization" => "Bearer token" }
config.webhook_allowed_hosts = ["hooks.slack.com", "*.example.com"] # optional
config.webhook_denied_hosts = ["blocked.example.com"]
# HTTP is only available in development, and only with this explicit opt-in:
config.webhook_allow_http_in_development = false
Webhook destinations must use HTTPS, cannot contain URL credentials, and are resolved and pinned to a validated public IP before connecting. Private, loopback, link-local, metadata, multicast, unspecified, reserved, and redirect destinations are rejected. Logged delivery URLs redact their path and query so provider tokens are not persisted.
POST JSON to each URL on every new exception:
{
"exception_class": "RuntimeError",
"controller_name": "users",
"action_name": "show",
"message": "Something went wrong",
"created_at": "2026-08-22T12:00:00Z",
"dashboard_url": "http://localhost:3000/rails_nexus/logged_exceptions/42"
}
Telegram
Set TELEGRAM_TOKEN and TELEGRAM_CHAT_ID environment variables:
export TELEGRAM_TOKEN="your-bot-token"
export TELEGRAM_CHAT_ID="your-chat-id"
Configure the notifier:
config.after_create = lambda do |exception|
RailsNexus::TelegramNotifier.new.notify(exception)
end
Rate limiting: 10-minute cooldown per exception class + controller combination.
Cleanup / Retention
config.retention_days = 90 # Auto-delete after 90 days
Rake tasks:
rake rails_nexus:cleanup # Delete exceptions older than retention_days
rake rails_nexus:stats # Show exception statistics
rake rails_nexus:tail # Tail JSON logs in real-time
rake rails_nexus:export # Export exceptions to JSON
rake rails_nexus:test_webhook # Test webhook configuration
Frontend setup
Hotwire (default)
RailsNexus ships a prebuilt JavaScript asset containing Turbo and its namespaced Stimulus controllers. It does not require importmap-rails, does not modify the host JavaScript entry point, and works in hosts using Importmap, jsbundling/esbuild/Bun, or no JavaScript bundler. The bundle reuses a host Stimulus application exposed as window.Stimulus; otherwise it starts one engine-scoped application and reuses it across repeated loads.
Styling
RailsNexus ships with its own CSS (rails_nexus/application.css) that works out of the box with dark/light themes. The engine layout does not depend on the host application's stylesheet.
Tailwind CSS
If your app uses Tailwind, add the gem's views as a source:
/* Tailwind v4 */
@import "tailwindcss";
@source "/path/to/rails_nexus/app/views";
Asset pipelines
The engine registers and precompiles its fingerprintable JavaScript and CSS assets automatically. Both Propshaft and Sprockets hosts can mount RailsNexus without changing the host asset manifest.
The entry point is rails_nexus/application; it does not replace or depend on the host app's application entry point. Turbo and Stimulus are served locally from their Rails gems rather than from a CDN.
Stimulus controllers
Four controllers are included and registered by the engine's entry point:
| Controller | Registered as | Purpose |
|---|---|---|
rails_nexus_controller.js |
rails_nexus |
Keyboard nav, search, tabs, copy, backtrace expand |
rails_nexus_detail_controller.js |
rails_nexus-detail |
Exception detail interactions |
rails_nexus_sidebar_controller.js |
rails_nexus-sidebar |
Mobile drawer toggle |
rails_nexus_theme_controller.js |
rails_nexus-theme |
Dark/light/auto theme toggle |
Data storage
The engine creates a rails_nexus_exceptions table with columns for exception class, controller/action, message, backtrace, request, environment, user information, user agent, remote IP, and timestamps.
RailsNexus applies Rails' configured parameter filters plus built-in password, token, authorization, cookie, session, and secret filters before persistence. Application-provided free-form messages can still contain sensitive data, so apply your normal database encryption, retention, backup, and access-control policies.
Analytics time grouping works on SQLite, PostgreSQL, and MySQL/MariaDB. Row counts work on all three; physical table-size and server uptime/connection metrics are displayed only where the adapter exposes them safely, and otherwise show as unavailable.
Security
The dashboard denies every request unless auth_block returns a truthy value. Webhooks use HTTPS by default, resolve and pin public destinations, reject redirects and private/reserved networks, and support explicit host allow/deny lists. See SECURITY.md for private vulnerability reporting.
Generators
| Generator | Description |
|---|---|
rails generate rails_nexus:install |
Full setup (migration, initializer, routes) |
rails generate rails_nexus:install --modern |
Install + generate all customizations |
rails generate rails_nexus:customize |
Generate customizable views, layout, and JS |
rails generate rails_nexus:customize --layout-only |
Only the layout |
rails generate rails_nexus:customize --views-only |
Only the views |
rails generate rails_nexus:customize --stimulus-only |
Only the Stimulus controller |
Development
Run the test suite:
cd /path/to/rails_nexus
RAILS_ENV=test bundle exec rails test
Build the gem:
gem build rails_nexus.gemspec
The repository includes a Rails dummy application under test/dummy for engine integration testing.
CI/CD
GitHub Actions runs the test matrix, database compatibility checks, asset builds, and security checks on pushes and pull requests. Releases are published to RubyGems when a version tag is pushed.
Before the first release, set the RubyGems account's MFA level to UI and gem
signin, then create an API key with only the Push rubygems scope, limited
to the rails_nexus gem. Leave MFA disabled for this CI key so RubyGems does
not request a one-time password during automated releases. Add the key to the
GitHub repository (or its release environment) as an Actions secret named
RUBYGEMS_API_KEY. Never commit the key to the repository.
The publish command has no interactive input. A missing key or an account/key that requires API MFA therefore fails the release instead of waiting for an OTP prompt.
Create the release environment in GitHub and add required reviewers if you
want a manual approval before publishing.
To release a version, update lib/rails_nexus/version.rb and CHANGELOG.md,
merge the changes to main, then push a matching tag:
git tag v2.1.4
git push origin v2.1.4
The workflow verifies that the tag matches the gem version, runs the tests, builds and validates the gem, then publishes it and creates the GitHub release.
Support
If you find RailsNexus useful, consider supporting the project:
- ⭐ Star the repo — Help others discover RailsNexus
- 🐛 Report issues — Help improve the gem
- 💡 Contribute — Submit pull requests
- 💰 Sponsor — GitHub Sponsors
Your support helps maintain and improve RailsNexus for the Rails community.
License
RailsNexus is released under the MIT License.