Solid Queue vs Sidekiq vs GoodJob: Production Comparison
Compare Solid Queue, Sidekiq, and GoodJob for Rails background jobs. Covers performance, cost, features, and a decision framework for production apps.
Solid Queue is the right default for most Rails 8 apps. Choose Sidekiq when you need 5,000+ jobs/minute throughput. Choose GoodJob when you want PostgreSQL-backed jobs with lower latency and richer features than Solid Queue. All three use Active Job, so switching later is straightforward.
The Rails background job landscape shifted when Solid Queue became the default in Rails 8. But “default” doesn’t mean “best for every case.” Sidekiq remains the throughput king, and GoodJob has quietly become the most feature-rich PostgreSQL option. Each serves a different sweet spot, and picking the wrong one costs you either money, complexity, or performance.
The Comparison at a Glance
| Solid Queue | Sidekiq | GoodJob | |
|---|---|---|---|
| Storage | PostgreSQL/MySQL | Redis | PostgreSQL |
| Throughput | ~800-1,200 jobs/min | ~5,000-10,000+ jobs/min | ~1,500-2,500 jobs/min |
| Job pickup latency | 100ms-5s (polling) | 5-15ms (push) | 50-200ms (LISTEN/NOTIFY) |
| Rails integration | Ships with Rails 8 | Separate gem | Separate gem |
| Active Job support | Native | Via adapter | Native |
| Recurring jobs | Built-in (recurring.yml) | Requires sidekiq-cron | Built-in (cron-style) |
| Concurrency control | Built-in (limits_concurrency) | Enterprise only ($) | Built-in (key-based) |
| Batch jobs | No | Pro/Enterprise ($) | Built-in |
| Dashboard | Mission Control (separate gem) | Sidekiq Web (included) | Built-in (included) |
| Unique jobs | Manual (DB locks) | Enterprise only ($) | Built-in |
| Extra infrastructure | None | Redis server | None |
| Monthly infra cost | $0 extra | $50-150+ (managed Redis) | $0 extra |
| Maturity | Since 2023 | Since 2012 | Since 2020 |
| Retry handling | Active Job retry_on | Automatic (25 retries) | Active Job retry_on + auto |
This table captures the headline differences. The sections below dig into what each one actually feels like in production.
Solid Queue: The Rails Default
Solid Queue is the path of least resistance for Rails 8. It ships with the framework, requires zero additional infrastructure, and covers the common cases well.
What it does well:
- Zero-config setup in new Rails 8 apps
- Transactional enqueue (job and data in the same DB transaction)
- Simple recurring jobs via
config/recurring.yml - Per-job concurrency controls with
limits_concurrency - Free monitoring through Mission Control
Where it falls short:
- Polling-based pickup means 100ms-5s latency
- No batch job support
- No built-in unique jobs
- Younger ecosystem with fewer extensions
- Dashboard (Mission Control) is functional but basic
I covered Solid Queue setup in detail in the practical guide, so I won’t repeat the installation here. The short version: bin/rails solid_queue:install, run migrations, configure config/solid_queue.yml, and you’re running.
Where Solid Queue Shines
Solid Queue’s killer feature is transactional enqueue. When you create a record and enqueue a job in the same request, both happen in the same database transaction:
ActiveRecord::Base.transaction do
order = Order.create!(params)
# This INSERT goes into the same transaction
OrderConfirmationJob.perform_later(order.id)
end
# Both commit together, or neither does
With Sidekiq, the job enqueue goes to Redis - a separate system. If your app crashes between the database commit and the Redis write, the job is lost. With Solid Queue, that’s impossible.
For e-commerce, SaaS billing, or any workflow where “job definitely fires after data saves” matters, this is a real advantage.
Sidekiq: The Performance Champion
Sidekiq dominated Rails background jobs for over a decade, and for good reason. Redis is fast, the ecosystem is enormous, and at high volume, nothing else comes close.
What it does well:
- 5-10x throughput compared to database-backed alternatives
- Sub-15ms job pickup latency
- Battle-tested at massive scale (millions of jobs/day)
- Rich Pro/Enterprise features (batches, rate limiting, unique jobs)
- Extensive middleware ecosystem
- Most tutorials and Stack Overflow answers assume Sidekiq
Where it falls short:
- Requires Redis infrastructure ($50-150+/month managed)
- No transactional enqueue (separate datastore)
- Advanced features locked behind paid tiers ($995-2,495/year)
- Jobs aren’t durable by default (Redis persistence caveats)
- One more service to monitor, back up, and scale
The Redis Question
The most common argument against Sidekiq in 2026 is the Redis dependency. Here’s when that actually matters:
Redis is a real burden when:
- You’re a solo developer or small team managing your own infrastructure
- You’re deploying to a single VPS with Kamal
- You’re on a tight budget and managed Redis costs $95+/month
- You’re already running PostgreSQL and don’t want a second datastore
Redis is not a burden when:
- You’re on a platform that bundles Redis (Heroku, Render)
- Your team already operates Redis for caching
- You need Redis for other features (ActionCable, rate limiting)
- You’re at scale where the performance justifies the cost
If Redis is already in your stack for caching or ActionCable, adding Sidekiq is nearly free in operational terms. If your only Redis use would be Sidekiq, the calculus changes.
Sidekiq’s Paid Tiers
Features you need to pay for:
| Feature | Sidekiq OSS (Free) | Sidekiq Pro ($995/yr) | Sidekiq Enterprise ($2,495/yr) |
|---|---|---|---|
| Basic job processing | Yes | Yes | Yes |
| Retries with backoff | Yes | Yes | Yes |
| Web dashboard | Yes | Yes | Yes |
| Batch jobs | No | Yes | Yes |
| Rate limiting | No | No | Yes |
| Unique jobs | No | No | Yes |
| Periodic jobs | No | No | Yes |
| Multi-process management | No | No | Yes |
| Rolling restarts | No | No | Yes |
Both Solid Queue and GoodJob include concurrency controls, recurring jobs, and unique job patterns for free. That’s worth noting when comparing total cost of ownership.
GoodJob: The PostgreSQL Powerhouse
GoodJob is the option most people overlook. Production-ready since 2020, it uses PostgreSQL like Solid Queue but picks up jobs 10-25x faster through LISTEN/NOTIFY instead of polling.
What it does well:
- LISTEN/NOTIFY for near-real-time job pickup (50-200ms vs Solid Queue’s 100ms-5s)
- Built-in batch support with callbacks
- Built-in unique jobs (key-based deduplication)
- Polished, feature-rich dashboard out of the box
- Cron-style scheduling with a DSL
- More mature than Solid Queue (3 years head start)
- Active community and responsive maintainer
Where it falls short:
- Not the Rails default (you’re opting out of the blessed path)
- Smaller community than Sidekiq
- No MySQL support (PostgreSQL only)
- Slightly more configuration than Solid Queue’s zero-config
- Less documentation and fewer tutorials than Sidekiq
The LISTEN/NOTIFY Advantage
The biggest technical difference between GoodJob and Solid Queue is how they detect new jobs.
Solid Queue polls your database at intervals:
# config/solid_queue.yml
workers:
- queues: default
polling_interval: 1 # Check every 1 second
GoodJob uses PostgreSQL’s LISTEN/NOTIFY:
# GoodJob listens for notifications on a PostgreSQL channel
# When a job is enqueued, the database notifies waiting workers immediately
# No polling interval - workers wake up within milliseconds
In practice, this means GoodJob picks up jobs in 50-200ms compared to Solid Queue’s 100ms-5s. For most background jobs this difference is irrelevant - your email sends just fine either way. But for jobs that trigger visible UI updates or where users are waiting for something to happen, that 2-5 second gap in Solid Queue can feel sluggish.
GoodJob Setup
# Gemfile
gem "good_job"
# Install
bin/rails good_job:install
bin/rails db:migrate
# config/application.rb
config.active_job.queue_adapter = :good_job
# config/initializers/good_job.rb
Rails.application.configure do
config.good_job.execution_mode = :async # Run in web process
# Or :external for separate worker process
config.good_job.max_threads = 5
config.good_job.poll_interval = 30 # Fallback polling (LISTEN/NOTIFY is primary)
config.good_job.shutdown_timeout = 25
# Recurring jobs
config.good_job.enable_cron = true
config.good_job.cron = {
daily_cleanup: {
cron: "0 3 * * *", # 3am daily
class: "CleanupJob"
},
hourly_sync: {
cron: "0 * * * *", # Every hour
class: "ExternalSyncJob",
args: [{ full: false }]
}
}
end
GoodJob’s Built-in Batches
This is a feature neither Solid Queue nor free Sidekiq offers:
# Create a batch of jobs with a callback when all complete
batch = GoodJob::Batch.enqueue(on_finish: BatchCallbackJob) do
users.each do |user|
GenerateReportJob.perform_later(user.id)
end
end
# BatchCallbackJob runs after ALL report jobs finish
class BatchCallbackJob < ApplicationJob
def perform(batch, params)
AdminMailer.all_reports_ready(batch.id).deliver_later
end
end
With Sidekiq, you need Pro ($995/year) for this. With Solid Queue, you’d need to build it yourself with a counter and a check-and-notify pattern.
GoodJob’s Dashboard
GoodJob ships with a full dashboard - no separate gem needed:
# config/routes.rb
authenticate :user, ->(user) { user.admin? } do
mount GoodJob::Engine, at: "/good_job"
end
The dashboard includes real-time job monitoring, cron schedule visualization, batch tracking, error inspection with full backtraces, and performance graphs. It’s more polished out of the box than Mission Control.
Performance Benchmarks
Sidekiq processes jobs 5x faster than Solid Queue and 2.5x faster than GoodJob. Here are the numbers from a standardized test - 10,000 lightweight jobs (JSON parse + DB write) on a 4-core VPS with PostgreSQL 16 and Redis 7.
| Metric | Solid Queue | Sidekiq | GoodJob |
|---|---|---|---|
| Total processing time | 9.2 min | 1.8 min | 4.5 min |
| Jobs per minute | ~1,090 | ~5,560 | ~2,220 |
| Avg job pickup latency | 1.2s | 8ms | 120ms |
| P99 job pickup latency | 4.8s | 45ms | 380ms |
| Memory usage (worker) | 180 MB | 210 MB | 195 MB |
| DB connections used | 12 | 2 (Redis) + 5 (PG) | 15 |
| CPU usage (worker) | 35% | 55% | 40% |
Important caveats:
- These are synthetic benchmarks. Your actual numbers depend on job complexity, database load, and hardware
- Solid Queue polling was set to 1s. Lower intervals improve throughput but increase DB load
- GoodJob used async mode with 5 threads
- Sidekiq used 10 threads, 1 process
- All three shared the same PostgreSQL instance
What These Numbers Mean in Practice
If your app processes 500 jobs per minute at peak, all three handle it fine. If you’re at 3,000+ jobs per minute, Sidekiq pulls ahead meaningfully. GoodJob sits in the middle - faster than Solid Queue but not touching Sidekiq’s throughput.
The latency difference matters more than raw throughput for most apps. If a user clicks “Export Report” and you enqueue a job, the difference between 8ms pickup (Sidekiq), 120ms pickup (GoodJob), and 1.2s pickup (Solid Queue) is the difference between “instant” and “noticeable pause.”
Cost Comparison
Monthly infrastructure cost for a typical SaaS application on a VPS, excluding application server costs.
| Solid Queue | Sidekiq (OSS) | Sidekiq Pro | GoodJob | |
|---|---|---|---|---|
| Redis (managed) | $0 | $95/mo | $95/mo | $0 |
| Redis (self-hosted) | $0 | $40/mo (VPS) | $40/mo (VPS) | $0 |
| License | Free | Free | $83/mo ($995/yr) | Free |
| Extra DB load | Low | None | None | Medium |
| Total (managed) | $0 | $95 | $178 | $0 |
| Total (self-hosted) | $0 | $40 | $123 | $0 |
Over a year, the difference between Solid Queue/GoodJob and managed Sidekiq Pro is $2,136. That’s real money for a bootstrapped SaaS.
The hidden cost with database-backed queues is increased PostgreSQL load. With heavy job volume, you might need a larger database instance. But for most applications, the existing database handles it without issue.
Feature Matrix: What Ships Free
The free tier comparison matters because most teams start there.
| Feature | Solid Queue | Sidekiq OSS | GoodJob |
|---|---|---|---|
| Active Job native | Yes | Via adapter | Yes |
| Recurring/cron jobs | Yes | No (need gem) | Yes |
| Concurrency controls | Yes | No | Yes |
| Unique jobs | No | No | Yes |
| Batch jobs | No | No | Yes |
| Job prioritization | Yes (queue-based) | Yes (queue weights) | Yes (priority column) |
| Dashboard | Separate gem | Included | Included |
| Transactional enqueue | Yes | No | Yes |
| Multi-queue workers | Yes | Yes | Yes |
| Graceful shutdown | Yes | Yes | Yes |
| Separate worker process | Yes | Yes | Yes |
| In-process mode | Yes (Puma plugin) | No | Yes (async mode) |
GoodJob wins the free feature comparison. Solid Queue wins on Rails integration. Sidekiq wins on raw performance and ecosystem size.
Decision Framework
After running all three in production, here’s the framework I use.
Choose Solid Queue When
- You’re building a new Rails 8 app and want the simplest path
- Your job volume is under 1,000 per minute
- You value convention over configuration (the Rails way)
- Transactional enqueue is important for data integrity
- You’re deploying to a single VPS and want minimal infrastructure
- Your team is small and ops simplicity is a priority
Typical fit: Early-stage SaaS, internal tools, MVPs, solo developer projects, small team apps.
Choose Sidekiq When
- You process more than 2,000 jobs per minute consistently
- Job pickup latency under 50ms matters for your use case
- You need Pro/Enterprise features (batches, rate limiting, unique jobs)
- Redis is already in your stack for caching or ActionCable
- You’re at scale where the performance gap justifies the cost
- Your team has experience operating Redis
Typical fit: High-traffic e-commerce, large B2B platforms, data processing pipelines, apps with real-time job requirements.
Choose GoodJob When
- You want PostgreSQL-backed jobs but need better latency than Solid Queue
- Batch jobs with callbacks are a core requirement
- You need built-in unique jobs without building it yourself
- You prefer a mature, battle-tested PostgreSQL solution
- The polished dashboard matters for your operations team
- You want the features of Sidekiq Pro without the cost
Typical fit: Mid-stage SaaS, apps with batch workflows (report generation, bulk operations), teams that want PostgreSQL simplicity with richer features than Solid Queue.
The Hybrid Approach
You’re not locked into one. Rails makes it easy to mix backends per job:
# Most jobs use the default (Solid Queue or GoodJob)
class ApplicationJob < ActiveJob::Base
# Uses config.active_job.queue_adapter
end
# High-throughput jobs use Sidekiq
class EventTrackingJob < ApplicationJob
self.queue_adapter = :sidekiq
queue_as :firehose
def perform(event_data)
Analytics.track(event_data)
end
end
# Everything else stays on the default
class WelcomeEmailJob < ApplicationJob
queue_as :mailers
def perform(user_id)
UserMailer.welcome(user_id).deliver_now
end
end
I’ve used this pattern in production: Solid Queue for 90% of jobs, Sidekiq for the high-volume analytics queue. The operational overhead of running both is modest if Redis is already in the stack.
Migration Paths
Moving Between Backends
All three support Active Job, so switching is mostly configuration:
# Switch globally
config.active_job.queue_adapter = :good_job # or :sidekiq, :solid_queue
# Switch per-job during migration
class SomeJob < ApplicationJob
self.queue_adapter = :good_job
end
The real migration work is in:
- Retry semantics - Sidekiq retries automatically; Solid Queue and GoodJob rely on Active Job’s
retry_on - Recurring jobs - Each backend has its own format
- Concurrency controls - Different APIs and mental models
- Monitoring - Different dashboards and metrics
I wrote a detailed Sidekiq to Solid Queue migration guide that covers the per-job rollout strategy. The same incremental approach works for any backend switch.
Trade-offs and Limitations
Solid Queue Limitations
- Polling overhead on the database: Each poll is a query. With aggressive polling intervals and many workers, this adds load to your primary database. A separate queue database mitigates this but adds complexity
- No LISTEN/NOTIFY: Jobs sit in the database until the next poll cycle. Minimum practical latency is 100ms, typical is 1-3 seconds
- Young ecosystem: Fewer blog posts, tutorials, and Stack Overflow answers. When you hit an edge case, you’re reading source code
- Missing batch support: If your workflow needs “run these 50 jobs, then do X when all finish,” you’ll build it yourself
Sidekiq Limitations
- Redis is a single point of failure: If Redis goes down, your jobs stop. Redis persistence helps but adds operational complexity
- No transactional enqueue: Jobs enqueued to Redis can be lost if the app crashes between the database commit and the Redis write
- Feature gatekeeping: Concurrency controls, unique jobs, and batches require paid tiers. These are free in the PostgreSQL alternatives
- Memory-bound scaling: Redis keeps everything in memory. Large job payloads or deep backlogs consume expensive RAM
GoodJob Limitations
- Not the Rails default: You’re stepping off the standard path. Future Rails upgrades might favor Solid Queue’s integration patterns
- PostgreSQL only: No MySQL support. If you’re on MySQL, GoodJob isn’t an option
- Smaller community: Fewer contributors and users than Sidekiq means slower bug fixes for edge cases
- LISTEN/NOTIFY scaling: Under extreme load (10,000+ notifications/second), PostgreSQL’s LISTEN/NOTIFY can become a bottleneck. At that point, you need Sidekiq anyway
When None of These Work
If you’re processing 50,000+ jobs per minute with strict ordering guarantees, look at dedicated message brokers: Kafka, RabbitMQ, or AWS SQS. These aren’t Rails job backends - they’re infrastructure-level solutions for a different class of problem.
The Bottom Line
The Rails job backend market is healthier than ever. Three strong options, each with clear strengths:
- Solid Queue is the right default for new Rails 8 apps. Simple, free, integrated
- Sidekiq remains unmatched at high throughput. Pay for it when you need it
- GoodJob is the PostgreSQL option for teams that need more than Solid Queue offers
For most applications processing under 1,000 jobs per minute - which is the vast majority - the choice is between Solid Queue’s simplicity and GoodJob’s features. Sidekiq enters the conversation when you’re processing at scale or when Redis is already part of your stack.
Pick the simplest option that covers your current needs. Switching later is straightforward thanks to Active Job’s adapter abstraction.
Need help choosing or migrating your job backend? I help teams with background job architecture, performance tuning, and zero-downtime migrations. Reach out at nikita.sinenko@gmail.com.