How to set up rails_error_dashboard with SolidQueue — capturing errors from controllers AND background jobs, with optional database isolation.
Rails 8.1 ships with SolidQueue as the default background job backend. No more Redis. No more Sidekiq licensing questions. Just your database.
But here's a question nobody talks about: what happens when your background jobs fail?
Sure, SolidQueue tracks failed executions. But do you get a dashboard? Severity classification? Trend charts? Notifications? Priority scoring?
Not out of the box.
I built rails_error_dashboard to solve exactly this — a self-hosted, open-source error tracking gem that works entirely inside your Rails process. No external services, no monthly fees, no data leaving your servers.
In this post, I'll walk you through setting it up with a Rails 8.1 app using SolidQueue, including the optional separate database configuration for production-grade isolation.
What We're Building
By the end of this guide, you'll have:
- Automatic error capture from both controllers and SolidQueue jobs
- A full error dashboard at
/error_dashboardwith analytics, severity scoring, and workflow management - Optional: errors stored in a separate database from your app data
- Verified that SolidQueue's retry mechanism still works (errors are captured, not swallowed)
Prerequisites
- Ruby 3.2+ (tested with Ruby 4.0.1)
- Rails 7.0+ (this guide uses Rails 8.1.2)
- SolidQueue (ships with Rails 8.1 by default)
Part 1: Basic Setup (Same Database)
Step 1: Add the Gem
# Gemfile
gem "rails_error_dashboard"
bundle install
Step 2: Run the Installer
rails generate rails_error_dashboard:install
rails db:migrate
That's it. Seriously. The installer:
- Creates the initializer at
config/initializers/rails_error_dashboard.rb - Copies 19 migrations for error logs, occurrences, baselines, comments, and more
- Mounts the engine at
/error_dashboardin your routes
Step 3: Configure SolidQueue for Development
Rails 8.1 only configures SolidQueue as the queue adapter in production by default. For development testing, add it explicitly:
# config/environments/development.rb
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }
You also need the multi-database setup for the queue database in development:
# config/database.yml
development:
primary:
<<: *default
database: storage/development.sqlite3
queue:
<<: *default
database: storage/development_queue.sqlite3
migrations_paths: db/queue_migrate
Then load the queue schema:
rails db:schema:load:queue
Step 4: Start the Server with SolidQueue
The simplest way to run both Puma and SolidQueue together is the Puma plugin:
SOLID_QUEUE_IN_PUMA=1 rails server
This runs the SolidQueue supervisor (dispatcher + worker) inside the Puma process — perfect for development and single-server deployments.
Alternatively, run them separately:
# Terminal 1
rails server
# Terminal 2
bin/jobs
Step 5: Verify It Works
Visit http://localhost:3000/error_dashboard and log in with the default credentials (gandalf / youshallnotpass). Change these in your initializer before deploying to production.
To test error capture, create a simple failing job:
# app/jobs/test_error_job.rb
class TestErrorJob < ApplicationJob
queue_as :default
def perform
raise RuntimeError, "Test error from SolidQueue"
end
end
Enqueue it:
rails runner "TestErrorJob.perform_later"
Within seconds, the error should appear in your dashboard with:
- Error type and message
- Full backtrace
- Severity classification (auto-detected)
- Occurrence count
- Priority score
How Error Capture Works
The gem uses a dual-layer approach to ensure no error goes uncaptured:
Layer 1: Rails Error Subscriber
Rails 7+ introduced Rails.error.subscribe — a built-in error reporting API. The gem subscribes to this, which means it automatically captures errors from:
- Controllers (via
rescue_fromand error handling) - ActiveJob (SolidQueue, Sidekiq, etc.)
- Anywhere you use
Rails.error.handleorRails.error.record
# This happens automatically — you don't need to do anything
Rails.error.subscribe(RailsErrorDashboard::ErrorReporter.new)
Layer 2: Rack Middleware
A lightweight Rack middleware sits at the top of the middleware stack as a final safety net. If an exception escapes all the way up, it captures it and then re-raises it so Rails can still render the appropriate error page.
# Simplified version of what the middleware does
def call(env)
@app.call(env)
rescue => error
Rails.error.report(error, handled: false, source: "middleware")
raise # Always re-raise!
end
The Critical Rule: Always Re-raise
This is the most important design decision in the entire gem. When we capture an error from a background job, we never swallow the exception. We report it and re-raise it.
Why? Because of Sentry issue #1173. Sentry's Sidekiq middleware once swallowed exceptions, which meant Sidekiq thought failed jobs had succeeded — retries never happened. Data was silently lost.
We verified this explicitly: after our gem captures a SolidQueue job error, the error still appears in solid_queue_failed_executions. SolidQueue's retry mechanism is fully intact.
Part 2: Separate Database Setup (Production-Grade)
For production apps, you might want error data in a separate database. Benefits:
- Performance isolation — high-frequency error writes don't contend with your app's queries
- Independent scaling — put your error DB on a different server
- Different retention — aggressive cleanup of old errors without touching app data
- Security — separate access controls and backup schedules
Step 1: Add the Database Configuration
# config/database.yml
development:
primary:
<<: *default
database: storage/development.sqlite3
queue:
<<: *default
database: storage/development_queue.sqlite3
migrations_paths: db/queue_migrate
error_dashboard:
<<: *default
database: storage/development_error_dashboard.sqlite3
migrations_paths: db/error_dashboard_migrate
production:
primary:
<<: *default
database: storage/production.sqlite3
queue:
<<: *default
database: storage/production_queue.sqlite3
migrations_paths: db/queue_migrate
error_dashboard:
<<: *default
database: storage/production_error_dashboard.sqlite3
migrations_paths: db/error_dashboard_migrate
For PostgreSQL in production (recommended):
production:
primary:
adapter: postgresql
database: myapp_production
# ... your existing config
queue:
adapter: postgresql
database: myapp_queue_production
# ...
error_dashboard:
adapter: postgresql
database: myapp_errors_production
pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
username: <%= ENV['ERROR_DB_USER'] %>
password: <%= ENV['ERROR_DB_PASSWORD'] %>
host: <%= ENV['ERROR_DB_HOST'] %>
migrations_paths: db/error_dashboard_migrate
Step 2: Install with Separate Database Flag
rails generate rails_error_dashboard:install \
--separate-database \
--database error_dashboard
Or if you've already installed, update the initializer:
# config/initializers/rails_error_dashboard.rb
RailsErrorDashboard.configure do |config|
config.use_separate_database = true
config.database = :error_dashboard # Must match your database.yml key
# ...
end
Step 3: Move Migrations
The installer places migrations in db/migrate/ by default. For a separate database, move them to match your migrations_paths:
mkdir -p db/error_dashboard_migrate
mv db/migrate/*rails_error_dashboard* db/error_dashboard_migrate/
Step 4: Create and Migrate
rails db:create # Creates all databases
rails db:migrate # Runs migrations against the correct databases
Step 5: Verify Isolation
You can verify the databases are truly isolated:
# Check error_dashboard DB has the gem's tables
sqlite3 storage/development_error_dashboard.sqlite3 ".tables"
# => rails_error_dashboard_error_logs, rails_error_dashboard_applications, ...
# Check primary DB has NO error tables
sqlite3 storage/development.sqlite3 ".tables"
# => ar_internal_metadata, schema_migrations (clean!)
# Check queue DB has only SolidQueue tables
sqlite3 storage/development_queue.sqlite3 ".tables"
# => solid_queue_jobs, solid_queue_failed_executions, ...
Three databases, three concerns, zero cross-contamination.
What You Get Out of the Box
Once installed, the dashboard at /error_dashboard gives you:
- Error list with search, filtering by type/severity/status/timeframe, and batch operations
- Error details with full backtrace, occurrence timeline, and context
- Analytics with 7-day trend charts, severity breakdown, and spike detection
- Workflow — assign errors, set priority, add comments, snooze, resolve
- Multi-channel notifications — Slack, Email, Discord, PagerDuty, webhooks (all optional)
- Platform detection — automatically tags errors as iOS/Android/Web/API
- Dark mode — because we're not savages
Production Checklist
Before deploying:
- Change the default credentials in your initializer
-
Set
retention_daysto prevent unbounded growth (e.g.,config.retention_days = 90) - Consider async logging for high-throughput apps:
config.async_logging = true
config.async_adapter = :solid_queue # Use your existing SolidQueue!
- Configure notifications — at minimum, set up Slack or email for critical errors
- Consider error sampling if you're generating thousands of errors per day:
config.sampling_rate = 0.1 # Log 10% of non-critical errors
Common Gotchas
SolidQueue Queue Database Not Set Up
Rails 8.1 only configures the queue database in production. If you see Could not find table 'solid_queue_processes' in development, you need to:
- Add the
queuedatabase to your development config indatabase.yml - Add
config.solid_queue.connects_to = { database: { writing: :queue } }to your development environment - Run
rails db:schema:load:queue
Migrations Running Against the Wrong Database
When using separate databases, the gem's migrations must be in the directory matching your migrations_paths. If you see error tables in your primary database, move the migration files:
mv db/migrate/*rails_error_dashboard* db/error_dashboard_migrate/
CSRF Errors When Testing with curl
If you're testing the enqueue endpoint with curl and getting 422 errors, that's Rails' CSRF protection working correctly. Use rails runner or the Rails console instead:
rails runner "TestErrorJob.perform_later"
Try It Out
The gem is open source and free forever:
- GitHub: AnjanJ/rails_error_dashboard
-
RubyGems:
gem install rails_error_dashboard - Live Demo: rails-error-dashboard.anjan.dev (gandalf / youshallnotpass)
If you're running Rails 8.1 with SolidQueue, this is the easiest way to get production-grade error tracking without paying for a SaaS product. Five minutes of setup, zero monthly fees, and your data never leaves your servers.
Have questions or feedback? Open an issue on GitHub

Top comments (0)