Active Job

Sometimes a specific piece of code takes a long time to run but doesn’t need to run right away. A classic example is sending an email after creating an order at the end of an online shopping workflow. Sending an email can take a long time, but you don’t want your user to wait for that to happen within the controller. It makes more sense to use a queueing mechanism for these tasks.

Active Job provides such a queueing system. You can create jobs which are processed asynchronously.

In Rails 8 the default queue adapter is Solid Queue — a database-backed, production-ready adapter written by the Rails team. A fresh rails new myapp wires it up automatically (both the gem and the tables) and in production jobs are persisted to your database. For development Rails still uses the in-process :async adapter so you don’t even need a worker running to try things out. This chapter uses the development defaults.

Create a New Job

The quickest way to create a new job is the job generator. Let’s create an example job which waits for 10 seconds and then logs an info message:

$ rails new shop
  [...]
$ cd shop
$ bin/rails db:prepare
$ bin/rails generate job example
      invoke  test_unit
      create    test/jobs/example_job_test.rb
      create  app/jobs/example_job.rb

All jobs are created in the app/jobs directory. Please change the app/jobs/example_job.rb file accordingly:

app/jobs/example_job.rb
class ExampleJob < ApplicationJob
  queue_as :default

  def perform(*args)
    sleep 10
    logger.info "Just waited 10 seconds."
  end
end

You can test the job in your console with ExampleJob.perform_later which enqueues it:

$ bin/rails console
Loading development environment (Rails 8.1.3)
irb(main):001> ExampleJob.perform_later
Enqueued ExampleJob (Job ID: 21526c3c-...) to Async(default)
Performing ExampleJob (Job ID: 21526c3c-...) from Async(default) enqueued at 2026-04-19T11:30:00Z
=> #<ExampleJob:... @arguments=[], @job_id="21526c3c-...", @queue_name="default", @executions=0>

Now you have to wait 10 seconds to see the following output in the console:

Just waited 10 seconds.
Performed ExampleJob (Job ID: 21526c3c-...) from Async(default) in 10012.97ms
irb(main):002> exit
The file log/development.log also contains the logging output.

You’ll find a more concrete example of using jobs in the Action Mailer chapter where an email gets sent.

Set the time for future execution

The set method provides two arguments which can be used to set the execution of a job in the future:

  • wait

    ExampleJob.set(wait: 1.hour).perform_later
  • wait_until

    ExampleJob.set(wait_until: Date.tomorrow.noon).perform_later

Queues

A job can declare which queue it should live in:

class ExampleJob < ApplicationJob
  queue_as :low_priority
  # ...
end

With Solid Queue you can then control concurrency per queue from config/queue.yml:

config/queue.yml
default: &default
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 3
      processes: 1
      polling_interval: 0.1

For development you do not need to edit this file. For production you typically run one dedicated worker process per machine with bin/jobs (Rails 8 generates that binstub for you).

Running Jobs in Production

Because Solid Queue is database-backed, running jobs in production is as simple as starting the worker process next to the web process:

$ bin/jobs

bin/jobs picks up the configuration from config/queue.yml, and Kamal (Rails 8’s default deployer) already wires it up as a separate service — see Production.

Recurring Jobs

Solid Queue also understands a simple cron-style schedule file, config/recurring.yml:

config/recurring.yml
production:
  clean_soft_deletes:
    class: CleanSoftDeletesJob
    queue: background
    schedule: every day at 4am

The entries in this file are enqueued by the bin/jobs process as regular Active Jobs.

Configure a Different Backend

If for some reason you don’t want Solid Queue, the page https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html lists all available Active Job backends. To use one of them you have to install the needed gem. Here is an example for the popular Sidekiq (which uses Redis).

Add it to the Gemfile and run bundle install:

Gemfile
gem "sidekiq"
$ bundle install

In config/application.rb you can configure the adapter:

config/application.rb
require_relative "boot"

require "rails/all"

Bundler.require(*Rails.groups)

module Shop
  class Application < Rails::Application
    config.load_defaults 8.1

    # Use Sidekiq instead of Solid Queue
    config.active_job.queue_adapter = :sidekiq
  end
end

Then make sure a Redis server is running and start the Sidekiq worker with bundle exec sidekiq.

For the overwhelming majority of small and mid-sized Rails 8 applications the built-in Solid Queue is plenty. Only reach for an alternative backend when you have a specific reason (existing Redis infrastructure, specific enterprise requirements, or a workload that really needs something Solid Queue cannot express).