Rails 8.1 lets your background jobs learn to breathe - and pick back up exactly where they left off

Active Job Continuations in Rails 8.1 - What They Solve and How to Use Them

This is the second post in our deeper dive into Rails 8.1. After announcing the release and explaining what’s new, now it’s time to get hands-on.

Active Job Continuations in Rails 8.1 - What They Solve and How to Use Them
If you’ve ever deployed a Rails app while long-running jobs were still in progress, or watched an import silently restart halfway through, this feature might feel like a breath of fresh air. Rails 8.1 introduces Active Job continuations, an API that allows background jobs to pause and resume safely, rather than starting from scratch when something interrupts them. Let’s take a look at what they are, why they matter, and how to start using them in your own code.

What Problem Do Active Job Continuations Solve?

Most Rails apps today are powered by background jobs. They run imports, process images, send emails, or stream results from third-party APIs. These jobs often work through a queueing system like Sidekiq, Solid Queue, or GoodJob. Traditionally, if a job is interrupted, say because of a server restart, deploy, or timeout, it would typically start again from the very beginning. That’s fine for fast tasks, but a large CSV import or a batch of thousands of video uploads? Doing it twice frustrates everyone.​

With Active Job continuations, your job can checkpoint progress and pick up right where it left off next time it runs, rather than looping through everything again. It’s the difference between “resume download” and “start over”.

How Do Active Job Continuations Work?

Continuations work by letting you define discrete steps inside your job by including the ActiveJob::Continuable module and using the step method. Steps can be defined as blocks (inline logic), with a cursor (for processing collections), or by referencing methods. Progress across steps, and even inside steps when iterating, is saved by Rails, so your job restarts exactly where it left off.

Example: Resumable CSV Import

class ImportUsersJob < ApplicationJob
  include ActiveJob::Continuable

  def perform(file_path)
    # Step 1: Initialize import
    step :initialize do
      @users = CSV.read(file_path)
    end

    # Step 2: Process users row by row, checkpointing progress
    step :import_rows do |step|
      @users.drop(step.cursor).each_with_index do |row, idx|
        User.create!(id: row[0], name: row[1])
        step.advance! from: step.cursor + 1
      end
    end

    # Step 3: Finalize import
    step :finalize
  end

  private

  def finalize
    # Any wrap-up logic
    Rails.logger.info "Import completed!"
  end
end

Block steps are run once; cursor steps checkpoint so resumption continues just after the last successful record.

The step.advance! call ensures the cursor (progress) persists across restarts to prevent duplicates.​

When Should You Use Continuations?

Continuations are ideal for:
  • Long-running imports
  • Content migrations
  • Batch processing (data syncs, streams)
  • Multi-step workflows
They're likely overkill for jobs that are:
  • Idempotent (simple sends or cache refreshes)
  • Quick, atomic one-liners
  • Already chunked via existing batching strategies
Rule of thumb: If a job would annoy you by starting over again if interrupted, use a continuation!

Observability & Monitoring

Rails 8.1 also introduces structured event reporting. You can pair continuations with logging like:
Rails.event.notify("job_step_completed", job: self.class.name, cursor: step.cursor)
This enables tracing jobs step-by-step, observing live progress, and faster forensic debugging.

Caveats & Gotchas

  • Continuations are opt-in: You must define steps for Rails to track progress.
  • Your logic must be idempotent: Side effects may repeat, so steps should be safe to retry.
  • Not all adapters support continuations: Verify that your job backend is up-to-date with Rails 8.1. Sidekiq and Solid Queue already support this well.​
  • Job initialisation runs every time: Any code outside steps will run on each start and resume. Keep that logic safe and stateless, or inside a step if appropriate.​
Active Job continuations are one of those features that seem small but make a real difference in Production, something we highlighted in our earlier post announcing what’s new in Rails 8.1. They slot perfectly into Rails’ philosophy: elegant abstractions solving real-world challenges. If your app includes a long-running import, migration, sync, or workflow, upgrading jobs with continuations will immediately improve both reliability and developer sanity.

If you're thinking of adopting this feature, now’s a perfect time to check out our Rails 8.1 upgrade guide, which walks through how to safely migrate, test, and deploy the update in your app.