Ruby

Sidekiq for Ruby on Rails: Setup, Jobs & Monitoring (2026)

Sidekiq for Ruby on Rails: Setup, Jobs & Monitoring (2026)

Sidekiq is a background job framework for Ruby that processes jobs concurrently on threads, backed by Redis. In Rails, add gem "sidekiq", set config.active_job.queue_adapter = :sidekiq, enqueue with perform_async, and run the sidekiq process alongside your app server. Sidekiq 8 requires Ruby 3.2+ and Redis 7+.

That answer covers the mechanics. This guide covers the rest: writing, scheduling, and prioritizing jobs, when Solid Queue is the better choice for a Rails 8 app, and the three metrics that flag a queue in trouble. Every job snippet was run against Sidekiq 8.1 on Ruby 3.4.

The Basics of Sidekiq

Web requests should finish in milliseconds. Sending an email, resizing an image, or importing a CSV file takes longer than that. Sidekiq moves that work out of the request cycle. Your app enqueues a job and responds to the user, while a separate Sidekiq process performs the work in the background.

Each Sidekiq process runs jobs on a pool of threads, so one process can work many jobs concurrently. That thread-based model separates Sidekiq from older single-threaded Ruby job runners: a handful of processes can deliver high throughput.

One naming note will save you confusion in older tutorials: job classes include Sidekiq::Job. The Sidekiq::Worker name has been deprecated since Sidekiq 6.3, because “worker” could mean a process, a thread, or a job class.

Redis, an in-memory data store, acts as the queue. The Sidekiq client in your app pushes a small JSON payload to Redis, and Sidekiq server processes pull payloads off and execute them. Sidekiq 8 also accepts Valkey 7.2+ or Dragonfly 1.27+ as drop-in Redis alternatives, per the project README.

Sidekiq 8 itself brought three headline changes, per the Sidekiq changelog: a Web UI rewritten from scratch without the Bootstrap framework, job profiling with Vernier behind a new Profiles tab, and job metrics retained for up to 72 hours.

Note: Sidekiq depends on Redis for storage, but we won’t get into Redis server configuration and tuning in this article.

Installation and Setup of Sidekiq for Rails

Sidekiq 8 requires Ruby 3.2 or later, and a Redis 7.0+ server (or Valkey 7.2+, or Dragonfly 1.27+). With those in place, add Sidekiq to your application’s Gemfile:

Ruby
gem "sidekiq"

Then install it:

Shell
bundle install

Next, tell Active Job to use Sidekiq as its backend. Open config/application.rb and add this line inside the application class:

Ruby
config.active_job.queue_adapter = :sidekiq

Classes that include Sidekiq::Job don’t need this setting; they talk to Sidekiq directly. The adapter line is what routes Active Job features, like mailers with deliver_later, through Sidekiq.

By default, Sidekiq connects to Redis at localhost:6379, and it honors the REDIS_URL environment variable. To configure the connection explicitly, create an initializer at config/initializers/sidekiq.rb:

Ruby
Sidekiq.configure_server do |config|
  config.redis = { url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0") }
end
 
Sidekiq.configure_client do |config|
  config.redis = { url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0") }
end

The server block configures the Sidekiq process that executes jobs; the client block configures your Rails processes that enqueue them.

Finally, Sidekiq runs as its own process, alongside your app server. Start it from your project directory:

Shell
bundle exec sidekiq

In production, run this under your process manager (a Procfile entry, a systemd unit, or a container) so it restarts with your deploys.

Setting Up a Sidekiq Job

A Sidekiq job is a plain Ruby class with two ingredients: the Sidekiq::Job module and a perform method.

Ruby
class WelcomeEmailJob
  include Sidekiq::Job
 
  def perform(user_id)
    # The work to run in the background goes here.
  end
end

To queue a job, call perform_async on the class:

Ruby
WelcomeEmailJob.perform_async(42)

This pushes the job to the default queue and returns a job ID. The arguments you pass are serialized to JSON, stored in Redis, and handed to perform when a Sidekiq process picks the job up.

That JSON round-trip is why you should pass small, plain values: strings, numbers, booleans. Pass a user_id instead of a User object, and look the record up fresh inside perform.

Creating Your First Sidekiq Job

Let’s make a job that does something visible:

Ruby
class HelloNameJob
  include Sidekiq::Job
 
  def perform(name, times)
    times.times do
      puts "Hello, #{name}!"
    end
  end
end

HelloNameJob takes two arguments: name and times. When performed, it prints a greeting to the console the specified number of times. In a real application, perform would hold any code you want off the request path.

Enqueue it with the arguments perform expects:

Ruby
HelloNameJob.perform_async("Jeff", 5)

This enqueues a job that prints “Hello, Jeff!” five times, as soon as a Sidekiq thread is free.

To run a job later instead of now, use perform_in or perform_at:

Ruby
HelloNameJob.perform_in(5.minutes, "Meredith", 3)
 
HelloNameJob.perform_at(2.days.from_now, "Jeff", 2)

Here, Meredith gets her three greetings in five minutes, while Jeff waits two days for his. Scheduled jobs sit in a separate Redis sorted set until they’re due, then move to their queue.

Advanced Sidekiq Usage

Enqueueing work is half the story. Sidekiq also gives you per-job control over what happens on failure and which jobs go first.

Automatic Job Retries

If a job raises an unhandled exception, Sidekiq retries it automatically with exponential backoff: by default, 25 attempts spread over about 20 days, per the Sidekiq error-handling documentation. Transient failures, like a flaky external API, often heal on a later attempt.

You can tune the retry count per job with sidekiq_options:

Ruby
class SyncInvoicesJob
  include Sidekiq::Job
 
  sidekiq_options retry: 10
 
  def perform(account_id)
    # ...
  end
end

Here, a failing job is retried ten times before Sidekiq gives up. A job that exhausts its retries moves to the Dead set, where it waits for you to inspect, fix, and retry it by hand from the Web UI.

Retries have one design consequence: a job can run more than once. Write perform so a second execution doesn’t double-charge a card or send a duplicate email. Idempotency keys and database constraints both work.

Job Prioritization

Not all jobs are equally urgent. A password reset email should not wait behind ten thousand analytics rollups. Sidekiq handles this with named queues; assign a job to one with sidekiq_options:

Ruby
class ChargePaymentJob
  include Sidekiq::Job
 
  sidekiq_options queue: "critical"
 
  def perform(payment_id)
    # ...
  end
end

Jobs from ChargePaymentJob now land in the critical queue. Then tell the Sidekiq process which queues to work, and how to weigh them:

Shell
bundle exec sidekiq -q critical,3 -q default,1

With weights, Sidekiq checks critical three times as often as default, so urgent jobs move first while the rest still make progress. Listing queues without weights processes them in strict order instead, which risks starving the later queues. Queue weighting is covered in the advanced options documentation.

Sidekiq vs Solid Queue in Rails 8

New Rails 8 applications come with Solid Queue configured as the Active Job backend, so the question is now “should I replace the default?” The short answer: stay on Solid Queue when job volume is modest and you’d rather not operate Redis; move to Sidekiq for high throughput, automatic retries with backoff, or its ecosystem of extensions.

Here’s how the two compare:

Sidekiq 8Solid Queue
Backing storeRedis 7+ (or Valkey, Dragonfly), in memoryYour database (MySQL, PostgreSQL, or SQLite), using FOR UPDATE SKIP LOCKED
ThroughputTens of thousands of jobs per second in the project’s sidekiqload benchmarkBound by database write capacity; ample for most apps, lower at high volume
RetriesBuilt in: exponential backoff, 25 attempts by default, Dead set for failuresDelegated to Active Job’s retry_on; unhandled failures wait in a table for manual retry
EcosystemOver a decade of middleware and extensions, plus commercial Pro and Enterprise tiersNewer and smaller; pairs with Mission Control for a jobs dashboard
Rails-default statusOpt-in: set config.active_job.queue_adapter = :sidekiqConfigured by default in new Rails 8 apps

The comparison is lower-stakes than it looks, because both run under Active Job. If your jobs use the Active Job API, switching backends later means changing the adapter and migrating in-flight jobs, not rewriting job classes. Jobs that include Sidekiq::Job directly are faster, but tie you to Sidekiq. Sidekiq’s README benchmark shows native jobs processing roughly twice as fast as their Active Job equivalents.

Monitoring Sidekiq in Production

Background jobs fail quietly. A user notices a slow page immediately; nobody notices a stuck queue until the password reset emails stop arriving. That makes monitoring less an add-on and more part of the setup.

The Built-In Web UI

Sidekiq ships with a web dashboard showing queues, in-progress jobs, retries, scheduled jobs, and the Dead set. In Sidekiq 8, it also carries a Metrics tab with up to 72 hours of job metrics and a Profiles tab for Vernier job profiles. Mount it in config/routes.rb:

Ruby
require "sidekiq/web"
 
mount Sidekiq::Web => "/sidekiq"

The dashboard then lives at /sidekiq. It exposes job arguments and lets visitors delete or retry jobs, so never leave it public: wrap the mount in an authentication constraint before deploying.

The Three Metrics That Matter

Dashboards offer plenty of numbers. Three of them predict trouble:

Queue latency, not queue size. Latency is how long the oldest job in a queue has been waiting. Ten thousand queued jobs can be fine if they drain in seconds, while fifty jobs with climbing latency mean work is arriving faster than your workers finish it. Sidekiq exposes latency directly:

Ruby
require "sidekiq/api"
 
Sidekiq::Queue.new("critical").latency # seconds the oldest job has waited

Watch latency per queue, and alert on it; a latency alert on critical tells you password resets are late while the number is still small.

Job failure and retry rate. A steady trickle of retries is normal; a rising rate is an early warning. Growth in retries usually points at a failing dependency, and it precedes a pile-up in the Dead set. Track the trend, not the totals.

Worker memory. Sidekiq processes are long-lived, so memory bloat accumulates: large payloads, unbounded caches, and leaky libraries show up as a worker’s resident set size (RSS) climbing until the process restarts or is killed. Memory tracked per process over time separates a leak from a hungry job.

Where the Web UI Stops

The Web UI is a snapshot: limited history, no alerts. For production, pair it with an APM tool that stores these metrics over time.

AppSignal for Ruby does this for Sidekiq out of the box. The gem hooks into Sidekiq’s server middleware without extra configuration, and the integration also covers jobs enqueued through Active Job. You get a prebuilt Sidekiq dashboard with job duration, failed and retried jobs, queue latency, and Redis memory, plus anomaly alerts on any of those metrics:

Sample AppSignal Sidekiq Dashboard

The same setup works for Sidekiq apps that don’t run on Rails; the integration documentation covers the plain-Ruby configuration.

The Sidekiq Web UI shows what’s queued right now, but not how queue latency trends or why a worker’s memory keeps climbing. AppSignal’s Sidekiq integration captures those automatically — latency, failures, and memory per worker — so the jobs you just built stay observable in production.

A Sidekiq Use Case

Email is the classic Sidekiq workload. Messages with attachments or many recipients take real time to send, and none of that time belongs in a web request. Here’s a dedicated Sidekiq job for it:

Ruby
class ResetPasswordJob
  include Sidekiq::Job
 
  def perform(user_id)
    UserMailer.reset_password_email(user_id).deliver_now
  end
end

ResetPasswordJob calls deliver_now because the job itself is already in the background; enqueue it with ResetPasswordJob.perform_async(user.id) from a controller or model.

In a typical Rails application, though, you often don’t need a dedicated job class for mail. Action Mailer integrates with Active Job, and with the Sidekiq adapter configured, deliver_later enqueues through Sidekiq on its own:

Ruby
class UserMailer < ApplicationMailer
  def reset_password_email(user_id)
    @user = User.find(user_id)
    mail(to: @user.email, subject: "Reset your password")
  end
end
 
# elsewhere in your application
UserMailer.reset_password_email(user.id).deliver_later

Reach for a hand-written Sidekiq job when you want control the Active Job wrapper doesn’t give you: custom sidekiq_options for retries and queues, or the raw throughput of native jobs. For everything else, deliver_later keeps your code shorter.

Wrapping Up

In this post, we set up Sidekiq 8 in a Rails application, wrote and scheduled jobs, and tuned retries and queue priorities.

We also compared Sidekiq with Solid Queue, the Rails 8 default, and built a monitoring habit around the three metrics that matter in production: queue latency, failure rate, and worker memory.

Happy coding!

P.S. If you’d like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!

Frequently asked questions

What tools can I use to monitor Sidekiq job performance?
Sidekiq ships with a Web UI that shows queues, retries, and scheduled jobs in real time. For history and alerting, an APM tool like AppSignal tracks queue latency, job failure rates, and per-worker memory, and alerts you when a threshold is crossed.
Should I use Sidekiq or Solid Queue in Rails 8?
Use Solid Queue if you want the Rails default and no Redis dependency; it stores jobs in your database. Use Sidekiq for high job volume, automatic retries with exponential backoff, and a deep ecosystem. Both work through Active Job, so switching later is contained.
How do I set up Sidekiq in a Rails app in 2026?
Add the sidekiq gem to your Gemfile, point config.active_job.queue_adapter to :sidekiq, and run a Sidekiq process next to your app server with bundle exec sidekiq. Sidekiq 8 needs Ruby 3.2 or newer and Redis 7 or newer.

Published , Updated

Wondering what you can do next?

  • Share this article on social media
Jeff Morhous

Jeff Morhous

Our guest author Jeff Morhous is a Software Engineer writing code and fixing bugs to help patients get the medications they need. These days he's focused on making web applications with Ruby on Rails, but in the past he's used Swift, Java, and Kotlin for iOS and Android development.

All articles by Jeff Morhous

Become our next author!

Find out more
$appsignal install

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal