Latest in branch 8.1
8.1.3
Released 24 Mar 2026
(2 months ago)
SoftwareRuby on Rails
Version8.1
Status
Supported
Initial release8.1.0
22 Oct 2025
(7 months ago)
Latest release8.1.3
24 Mar 2026
(2 months ago)
End of bug fixes10 Oct 2026
(Ends in 4 months)
End of security fixes10 Oct 2027
(Ends in 1 year, 4 months)
Release noteshttps://github.com/rails/rails/releases/tag/v8.1.3
Source codehttps://github.com/rails/rails/tree/v8.1.3
Downloadhttps://github.com/rails/rails/releases/tag/v8.1.3
Ruby on Rails 8.1 ReleasesView full list

What Is New in Ruby on Rails 8.1

Rails 8.1 delivers a focused set of improvements centered on job resilience, structured observability, streamlined deployment, and cleaner codebases. Below is a high-level summary of what changed.

Category Highlights
New Features Active Job Continuations, Structured Event Reporting, Local CI DSL, Markdown Rendering in controllers, Command-line Credentials Fetching, Deprecated Associations, Registry-Free Kamal Deployments
Improvements Verbose redirect logs in development, alphabetically sorted columns in schema.rb, structured event tagging and context propagation
Breaking Changes Leading brackets in query strings no longer stripped, semicolons no longer supported as query string separators, routes to multiple paths removed, Azure storage service removed from Active Storage
Deprecations Built-in Sidekiq adapter (now in sidekiq gem), String#mb_chars, ActiveSupport::Multibyte::Chars, ActiveSupport::Configurable, order-less finder methods, insert_all/upsert_all with unpersisted records, WITH/DISTINCT in update_all

How Do Active Job Continuations Work in Rails 8.1?

Active Job Continuations let long-running jobs resume from the last completed step instead of restarting from scratch after an interruption. This is the single most operationally significant change in Rails 8.1 for teams running background jobs during deploys.

In practice, if you use Kamal -- which gives job containers only 30 seconds to shut down -- a large import or data migration job used to either fail mid-way or require complex custom checkpointing. Now you include ActiveJob::Continuable and define discrete steps:

class ProcessImportJob < ApplicationJob
  include ActiveJob::Continuable

  def perform(import_id)
    @import = Import.find(import_id)

    step :initialize do
      @import.initialize
    end

    step :process do |step|
      @import.records.find_each(start: step.cursor) do |record|
        record.process
        step.advance! from: record.id
      end
    end

    step :finalize
  end

  private
    def finalize
      @import.finalize
    end
end

The step.cursor is automatically saved when the job is interrupted, and the job resumes from that exact record on the next execution. Watch out for jobs that have side effects which are not idempotent -- continuations assume each step can be safely re-entered from its cursor position.

Most teams doing rolling deploys with a Solid Queue backend will want to audit existing long-running jobs and migrate them to this pattern. The cursor approach is especially well-suited to find_each loops over large datasets.

What Is the New Structured Event Reporting API in Rails 8.1?

Rails 8.1 introduces Rails.event, a unified interface for emitting structured events from anywhere in a Rails application -- a direct answer to the long-standing gap between human-readable logs and machine-parseable observability data.

The API supports three core primitives:

  • Notify: emit a named event with a structured payload (Rails.event.notify("user.signup", user_id: 123))
  • Tagged: wrap a block so all events inside carry extra tag metadata
  • Set context: attach ambient key-value pairs (e.g. request_id, tenant ID) to all events in scope

Applications control serialization by registering subscriber objects that implement an #emit method. This means you can fan out to Datadog, a log aggregator, and an internal event store simultaneously without coupling your domain code to any specific sink.

Rails.event.set_context(request_id: "abc123", shop_id: 456)

Rails.event.tagged("checkout") do
  Rails.event.notify("order.placed", order_id: order.id, total: order.total)
end

This matters if you are building multi-tenant SaaS products where attaching a shop_id to every emitted event used to require a custom middleware or thread-local hacks. The context API solves this cleanly at the framework level.

What Breaking Changes in Action Pack Should You Watch Out for When Upgrading to Rails 8.1?

Rails 8.1 ships several query string parsing behavior changes that were deprecated in earlier versions and are now fully removed -- these are the most likely to silently break API clients or form submissions.

Two changes deserve close attention before upgrading:

  • Leading brackets in query strings are no longer stripped. A query string like [foo]=bar previously parsed to { "foo" => "bar" }. It now parses to { "[foo]" => "bar" }. Any client sending bracket-prefixed keys without a proper leading key will break silently.
  • Semicolons are no longer treated as query string separators. foo=bar;baz=quux used to yield two separate params. Now the entire string after the semicolon is treated as part of the first param value. This affects older JavaScript libraries and some HTTP clients that generate semicolon-delimited query strings.
# Before Rails 8.1
ActionDispatch::ParamBuilder.from_query_string("[foo]=bar")
# => { "foo" => "bar" }

# After Rails 8.1
ActionDispatch::ParamBuilder.from_query_string("[foo]=bar")
# => { "[foo]" => "bar" }

The safest upgrade path is to run your full integration test suite with RAILS_ENV=test against Rails 8.1 before touching staging. Pay particular attention to any controller specs that parse query strings directly, and audit any third-party clients that send non-standard query string formats.

Active Storage teams should also note that the :azure storage service has been fully removed. If you are on Azure Blob Storage, you must migrate to the azure-blob gem adapter before upgrading.

How Do Deprecated Associations and the New Local CI Change Day-to-Day Rails Development?

Rails 8.1 adds two developer-experience improvements that are small in code footprint but large in practical impact: the ability to mark associations as deprecated, and a built-in local CI runner.

Deprecated Associations let you signal that a relationship is being phased out without immediately breaking anything. This is invaluable when refactoring a legacy data model:

class Author < ApplicationRecord
  has_many :posts, deprecated: true
end

Usage of author.posts, preloading via author.preload(:posts), and even nested attribute usage will all trigger a report. Three modes are available -- :warn (default), :raise, and :notify -- and you always get the source location of the offending call regardless of backtrace settings. Most teams will start with :warn during a migration window and switch to :raise before finally removing the association.

Local CI gives new Rails apps a config/ci.rb file and a bin/ci runner out of the box. The DSL is straightforward:

CI.run do
  step "Setup",            "bin/setup --skip-server"
  step "Style: Ruby",      "bin/rubocop"
  step "Security: Brakeman", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
  step "Tests: Rails",     "bin/rails test"

  if success?
    step "Signoff", "gh signoff"
  else
    failure "CI failed. Do not merge or deploy.", "Fix the issues and try again."
  end
end

The optional gh signoff integration blocks merges on GitHub until a passing CI run has explicitly signed off the PR. For small-to-mid-sized teams, this can completely replace a cloud CI provider for day-to-day feedback cycles.

What Active Record and Active Support Deprecations in Rails 8.1 Require Immediate Action?

Several Active Record and Active Support deprecations in Rails 8.1 will surface as warnings in your logs the moment you upgrade -- and a few of them may require non-trivial refactors before you can move to Rails 8.2.

On the Active Record side, watch out for:

  • Order-less finder methods: calling #first, #last, or similar methods without an explicit order scope is now deprecated. This affects a lot of convenience calls in controllers and seeds.
  • insert_all/upsert_all with unpersisted records in associations: this pattern is being removed. Ensure records are persisted before using bulk-insert APIs through associations.
  • WITH / WITH RECURSIVE / DISTINCT in update_all: these SQL features combined with update_all are deprecated. Refactor to raw SQL or scoped queries.
  • schema.rb columns now sorted alphabetically: this is not a deprecation but a behavior change. If your team diffs schema.rb in code review, expect a one-time large diff on the first run of db:schema:dump after upgrading.

On the Active Support side, String#mb_chars and ActiveSupport::Multibyte::Chars are deprecated. If your app handles multibyte string manipulation through these APIs (common in internationalized apps), you will need to migrate to Ruby's native encoding APIs or a dedicated gem.

The built-in Sidekiq adapter deprecation in Active Job is worth flagging separately: the adapter is being moved to the sidekiq gem itself. Add the latest version of the sidekiq gem to your Gemfile and remove any explicit adapter configuration pointing to the Rails built-in version.

Frequently Asked Questions about Ruby on Rails 8.1

What is the minimum prerequisite version before upgrading to Rails 8.1?
You must be running Rails 8.0 before upgrading to 8.1, and your application should have a solid test suite passing cleanly on 8.0 before attempting the upgrade to 8.1.

Do Active Job Continuations work with any queue backend or only Solid Queue?
Active Job Continuations are a framework-level feature and work with any backend that supports job retry and persistence, but they are designed with Kamal-based deployments and Solid Queue in mind. The cursor state is managed by the Continuable module itself, not the adapter, so compatibility depends on whether your adapter reliably retries interrupted jobs.

How do I enable verbose redirect logs in an existing Rails 8.1 app?
New apps get verbose redirect logging in development automatically. For existing apps, add config.action_dispatch.verbose_redirect_logs = true inside your config/development.rb file and restart the server.

Will my existing Sidekiq setup break immediately after upgrading to Rails 8.1?
No, the built-in Sidekiq adapter is deprecated, not removed -- you will see deprecation warnings, but jobs will continue to enqueue and process normally until the adapter is eventually removed in a future release. The fix is to ensure your Gemfile includes the latest sidekiq gem and that your adapter configuration defers to it.

How do I mark an association as deprecated and what modes are available?
Pass deprecated: true on any has_many, has_one, or belongs_to call. The three reporting modes are warn (default, writes to the log), raise (raises an exception on use, useful in test environments), and notify (sends the report through Rails.error.report for integration with error tracking services). You can set the mode with deprecated: { mode: :raise }.

What happens to the schema.rb file on the first db:schema:dump after upgrading to Rails 8.1?
Columns within each table are now sorted alphabetically, so your first schema dump will produce a large diff touching many tables even though no actual schema change was made. This is a cosmetic one-time change and safe to commit, but communicate it to your team to avoid confusion during code review.

Releases In Branch 8.1

VersionRelease date
8.1.324 Mar 2026
(2 months ago)
8.1.2.123 Mar 2026
(2 months ago)
8.1.208 Jan 2026
(4 months ago)
8.1.128 Oct 2025
(7 months ago)
8.1.022 Oct 2025
(7 months ago)
8.1.0.rc115 Oct 2025
(7 months ago)
8.1.0.beta104 Sep 2025
(8 months ago)