SolidQueue gives developers a high degree of control over how and when jobs are processed, making it suitable for sophisticated workload management without needing to reach for third-party infrastructure like Redis or Kubernetes autoscalers.
This control is possible because of SolidQueue’s reliance on the database and its tight integration with Rails’ ActiveJob API.
By default, jobs are processed in the order they are enqueued (FIFO), but SolidQueue enhances this with configurable priorities, queue ordering, pausing mechanisms, and concurrency tuning.
Understanding how to properly configure and manage your queues and job priorities is essential for building responsive, efficient background systems.
Whether you want high-priority jobs like user notifications to execute before long-running tasks like data exports, or need to temporarily pause a queue during deployments, SolidQueue provides powerful yet simple-to-use APIs to make that possible. Let’s explore how.
But before we move forward with complex concepts let's iterate over what is a queue, what is a queue priority, etc.
1. What is a Queue, and Why Do Jobs Need One?
A queue in SolidQueue is just a way to group jobs. When you add a job, it goes into a named queue. This helps keep different types of work organized.
By default, jobs go into a queue named default
. But you can create your own queues and name them anything — like emails
, exports
, or notifications
. This way, you can run some jobs separately or give more power (workers) to queues that need faster processing.
Example:
class SendNewsletterJob < ApplicationJob
queue_as :emails
def perform(user_id)
# code to send newsletter
end
end
With the queue_as
macro, you're explicitly categorizing your job. Workers can then be configured to only process specific queues, allowing for workload separation (e.g., notifications
, low_priority
, critical_tasks
, etc.).
2. What is Job Priority?
Let’s say five jobs are waiting in the same queue. How do you decide which one should go first? That’s what job priority does.
In SolidQueue, a lower number means a higher priority. So, a** job with priority 1 will run before a job with priority 10**, even if it was added later.
This is very helpful when you want important jobs (like sending a password reset email) to run ahead of less urgent ones (like cleaning up old logs).
Example:
SendNewsletterJob.set(priority: 1).perform_later(user.id)
You can use any number you want — the smaller it is, the sooner the job will run.
This is particularly useful when you have thousands of jobs piling up and want certain tasks to cut ahead in the line.
If you don’t give any job a priority, SolidQueue will just run jobs in the order they were added (FIFO: First-In, First-Out). So, the job that was enqueued first will be run first — unless you’ve paused the queue or set a concurrency limit (we’ll talk about that soon). This default behavior works fine for simple use cases.
3. Pausing and Resuming Queues
Sometimes, you want to temporarily pause a queue. Maybe you are fixing a bug or your email service is down, and you don’t want the job to keep trying and failing.
In that case, you can pause a queue like this:
SolidQueue::Queue.pause("emails")
This will stop workers from picking up jobs in the emails queue.
And when you're ready to start processing again:
SolidQueue::Queue.resume("emails")
To pause and resume queues it would generally be better to create custom rake
tasks which then can be used during CI/CD, as such:
a) Simple rake
task
#lib/tasks/solid_queue.rake
namespace :solid_queue do
desc "Pause emails queue"
task pause_emails: :environment do
SolidQueue::Queue.pause("emails")
puts "Paused 'emails' queue"
end
end
Then run it like this:
a) On rails console
RAILS_ENV=production bin/rake solid_queue:pause_emails
b) Advance rake
task
#lib/tasks/solid_queue_control.rake
namespace :solid_queue do
desc "Pause a specific queue"
task :pause, [:queue_name] => :environment do |t, args|
queue = args[:queue_name]
if queue
SolidQueue::Queue.pause(queue)
puts "Paused SolidQueue '#{queue}' queue"
else
puts "No queue name provided!"
exit(1)
end
end
desc "Resume a specific queue"
task :resume, [:queue_name] => :environment do |t, args|
queue = args[:queue_name]
if queue
SolidQueue::Queue.resume(queue)
puts "Resumed SolidQueue '#{queue}' queue"
else
puts "No queue name provided!"
exit(1)
end
end
end
Then run it like this inside your docker.yml
:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Pause SolidQueue Email Queue (Pre-Migration)
run: |
bundle exec rake solid_queue:pause[email]
env:
RAILS_ENV: production
DATABASE_URL: ${{ secrets.DATABASE_URL }}
4. Limiting How Many Jobs Run at Once (Concurrency)
Let’s say one of your jobs takes a lot of memory or connects to an external service that’s slow. You don’t want too many of these running at once— it can overload your app or bring external services to their knees.
SolidQueue lets you limit the number of jobs that run at the same time per worker using the --concurrency
flag. You can do this when starting the worker process manually:
bin/rails solid_queue:work --concurrency=5 --queues=emails
This command tells the worker: “Only pick up 5 jobs from the emails
queue at a time.”
You can start different workers for different queues, like so:
bin/rails solid_queue:work --concurrency=3 --queues=critical
bin/rails solid_queue:work --concurrency=2 --queues=low_priority
This gives you fine-grained control over how much parallel job load you're running across different types of work.
How to use concurrency in development environment?
In development, SolidQueue works inline by default. That means it executes jobs immediately in the same thread as your Rails app — no separate worker or polling loop needed.
So, when you do something like:
MyJob.perform_later(args)
…it actually just runs synchronously (like perform_now
) behind the scenes when you're using rails server
.
There’s no concurrency setting available in this inline mode — because it’s effectively single-threaded execution handled directly by Rails.
If you want to simulate concurrent job execution even in development (for testing), you can still manually run a worker process:
bin/rails solid_queue:work --concurrency=5
This will behave exactly like in production, polling jobs from the database instead of executing them inline.
5. Inspecting Queues and Priorities in Code
You can programmatically inspect queue statuses and job counts:
SolidQueue::Queue.all.each do |queue|
puts "#{queue.name} - paused: #{queue.paused?} - pending jobs: #{queue.jobs.pending.count}"
end
This can be especially useful for building custom dashboards or integrating queue metrics into your monitoring stack.
By organizing jobs into logical queues, assigning priorities, and leveraging pausing and concurrency features, you can build a highly responsive background job processing layer that handles workloads gracefully and predictably—all without leaving Rails or reaching for additional infrastructure.