Ruby on Rails 8: New Features and Migration Guide 2026

Ruby on Rails 8 ships with the Solid Trifecta, a built-in authentication generator, Kamal 2 deployment, and Propshaft. This tutorial covers every major feature and walks through upgrading from Rails 7 step by step.

Ruby on Rails 8 new features and migration guide illustration

Ruby on Rails 8 represents the most significant release since Rails 7, introducing database-backed replacements for Redis, native authentication, and a streamlined deployment pipeline. Released in November 2024 and now mature with the Rails 8.1 follow-up in October 2025, Rails 8 eliminates external dependencies that previously required dedicated infrastructure.

Rails 8 at a Glance

Rails 8 replaces Redis with the Solid Trifecta (Solid Queue, Solid Cache, Solid Cable), adds a built-in authentication generator, switches the default asset pipeline to Propshaft, and ships Kamal 2 for zero-downtime deployment. Ruby 3.2+ is required.

The Solid Trifecta: Database-Backed Infrastructure

Before Rails 8, most production applications depended on Redis for background jobs, caching, and WebSocket pub/sub. The Solid Trifecta replaces all three with database-backed adapters that work with PostgreSQL, MySQL, and SQLite.

This architectural shift reduces operational complexity. No more Redis instance to monitor, scale, and maintain. The tradeoff is throughput: Redis remains faster for high-volume scenarios, but for the vast majority of applications, database-backed adapters perform more than adequately.

Solid Queue for Background Jobs

Solid Queue replaces Sidekiq, Resque, or Delayed Job with a database-backed Active Job backend. It leverages FOR UPDATE SKIP LOCKED on PostgreSQL 9.5+, MySQL 8.0+, and falls back gracefully on SQLite.

ruby
# config/environments/production.rb
config.active_job.queue_adapter = :solid_queue

# config/solid_queue.yml
production:
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 5
      processes: 2
      polling_interval: 0.1

The Puma plugin starts Solid Queue alongside the web server in development, removing the need for a separate process:

ruby
# config/puma.rb
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] || Rails.env.development?

Solid Queue supports recurring jobs, concurrency controls, and mission-critical features like pause/resume per queue. For applications processing fewer than 10,000 jobs per minute, it handles the load without issue.

Solid Cache and Solid Cable

Solid Cache stores HTML fragment caches in the database instead of Memcached or Redis. Disk storage (SSD/NVMe) costs a fraction of RAM, enabling larger cache pools with longer retention. Basecamp runs a 10 TB Solid Cache with 60-day retention in production.

ruby
# config/environments/production.rb
config.cache_store = :solid_cache_store

Solid Cable replaces the Redis pub/sub adapter for Action Cable. WebSocket messages are written to a database table and polled every 100ms by default. Near real-time for most use cases, with messages pruned after 24 hours.

yaml
# config/cable.yml
production:
  adapter: solid_cable
  polling_interval: 0.1
  keep_messages_around_for: 1.day

Built-in Authentication Generator

Rails 8 ships a native authentication generator that produces session-based login, logout, and password reset scaffolding. No external gems required for basic authentication.

bash
# Generate the authentication scaffolding
bin/rails generate authentication

This command creates the User model, Session model, SessionsController, PasswordsController, and an Authentication concern. The generated code uses has_secure_password with bcrypt and includes rate limiting (10 login attempts per 3 minutes per IP).

ruby
# app/controllers/concerns/authentication.rb
module Authentication
  extend ActiveSupport::Concern

  included do
    before_action :require_authentication
    helper_method :authenticated?
  end

  private

  def require_authentication
    resume_session || request_authentication
  end

  def resume_session
    Current.session = find_session_by_cookie
  end

  def find_session_by_cookie
    Session.find_by(id: cookies.signed[:session_id]) if cookies.signed[:session_id]
  end

  def request_authentication
    session[:return_to_after_authenticating] = request.url
    redirect_to new_session_path
  end

  def authenticated?
    Current.session.present?
  end
end

Password resets use signed, time-limited tokens generated by has_secure_password rather than database-stored tokens. The generator does not include sign-up, email verification, or social login. These are intentional omissions: the generated code serves as a foundation to extend, not a Devise replacement.

Authentication Scope

The built-in generator covers login, logout, and password reset. Registration, email verification, OAuth, and MFA must be added manually or via gems like Authentication Zero.

Safer Parameter Handling with params.expect

Rails 8 introduces params.expect, a safer alternative to the params.require.permit pattern. The new method raises on missing keys instead of silently returning nil.

ruby
# Before (Rails 7)
def user_params
  params.require(:user).permit(:name, :email, :role)
end

# After (Rails 8)
def user_params
  params.expect(user: [:name, :email, :role])
end

The expect method enforces that the :user key exists and contains only the permitted attributes. If the key is missing, it raises ActionController::ParameterMissing immediately, preventing silent bugs downstream.

Propshaft: The New Default Asset Pipeline

Propshaft replaces Sprockets as the default asset pipeline. Where Sprockets handled compilation, minification, and fingerprinting in a single monolithic system, Propshaft focuses exclusively on serving and fingerprinting static assets.

For JavaScript bundling, Propshaft delegates to modern tools: esbuild, Vite, or Bun. CSS processing goes through Tailwind CLI or dart-sass. The result is a faster, more predictable asset pipeline that aligns with current frontend tooling.

ruby
# Gemfile (Rails 8 default)
gem "propshaft"

# No configuration needed for basic usage
# Assets in app/assets are served and fingerprinted automatically

Existing applications using Sprockets can continue to do so. The migration path involves removing Sprockets-specific directives (//= require) and relying on import maps or a JavaScript bundler.

Ready to ace your Ruby on Rails interviews?

Practice with our interactive simulators, flashcards, and technical tests.

Kamal 2 and Thruster: Zero-Downtime Deployment

Rails 8 ships preconfigured with Kamal 2, a deployment tool that provisions a fresh Linux box into a production server with a single command. Kamal 2 replaces Traefik with Kamal Proxy, a purpose-built reverse proxy.

bash
# Deploy to a fresh server
kamal setup

# Subsequent deployments
kamal deploy

Thruster sits between Kamal Proxy and Puma, providing X-Sendfile acceleration for file downloads, automatic asset compression (gzip/brotli), and HTTP/2 support. The Dockerfile generated by Rails 8 includes Thruster by default.

dockerfile
# Excerpt from Rails 8 generated Dockerfile
RUN gem install thruster
CMD ["thrust", "./bin/rails", "server"]

This stack (Kamal 2 + Kamal Proxy + Thruster + Puma) handles SSL termination, zero-downtime deploys, and rolling restarts without external services like Nginx or HAProxy.

Enhanced SQLite for Production

Rails 8 improves SQLite support for production use. Connection pooling, WAL mode, and improved concurrency make SQLite viable for small to medium applications. Combined with the Solid Trifecta, a single-server Rails 8 application can run entirely on SQLite with no external database server.

yaml
# config/database.yml (SQLite production setup)
production:
  primary:
    adapter: sqlite3
    database: storage/production.sqlite3
    pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  cache:
    adapter: sqlite3
    database: storage/production_cache.sqlite3
  queue:
    adapter: sqlite3
    database: storage/production_queue.sqlite3
  cable:
    adapter: sqlite3
    database: storage/production_cable.sqlite3

This configuration runs the entire application stack from four SQLite files. Kamal mounts the storage directory as a persistent volume, ensuring data survives container restarts.

Upgrading from Rails 7 to Rails 8

The migration path from Rails 7.2 to Rails 8.0 is straightforward if the application already addresses deprecation warnings. Applications on Rails 7.0 or 7.1 should upgrade incrementally: 7.0 to 7.1, then 7.2, then 8.0.

Pre-Upgrade Checklist

Rails 8 requires Ruby 3.2+. Verify Ruby version, ensure tests pass on Rails 7.2, and check gem compatibility with RailsBump before starting the upgrade.

Step 1: Update Dependencies

ruby
# Gemfile
gem "rails", "~> 8.0"
bash
bundle update rails

Step 2: Run the Update Task

bash
bin/rails app:update

This command updates configuration files to Rails 8 defaults. Review each change carefully using git diff. Key changes include the new Regexp.timeout default (1 second), updated db:migrate behavior (schema load on fresh databases), and Propshaft configuration.

Step 3: Handle Schema Reordering

Rails 8 reorders schema.rb columns to match actual database column order. Run the schema dump immediately to isolate this cosmetic diff from real migration changes:

bash
bin/rails db:schema:dump
git add db/schema.rb
git commit -m "Reorder schema columns for Rails 8"

Step 4: Adopt New Features Incrementally

The Solid Trifecta, Propshaft, and authentication generator are opt-in for existing applications. Adopt them individually after the core upgrade is stable:

  1. Replace the job adapter with Solid Queue
  2. Switch cache store to Solid Cache
  3. Migrate Action Cable to Solid Cable (if applicable)
  4. Evaluate Propshaft migration for the asset pipeline

Step 5: Update Parameter Handling

Replace params.require.permit calls with params.expect across controllers. This change is optional but recommended for stronger parameter validation.

Rails 8.1: Continuable Jobs and Built-in CI

Rails 8.1, released October 2025, builds on the Rails 8 foundation with two headline features. Continuable Jobs (ActiveJob::Continuable) split long-running jobs into resumable steps. If a server restarts mid-import, the job picks up exactly where it stopped instead of restarting from scratch.

ruby
# app/jobs/import_records_job.rb
class ImportRecordsJob < ApplicationJob
  include ActiveJob::Continuable

  def perform(cursor:)
    records = Record.where("id > ?", cursor.value || 0).limit(1000)

    records.each do |record|
      process(record)
      cursor.advance(record.id)
    end
  end
end

Rails 8.1 also introduces built-in CI configuration and native Markdown rendering, further reducing external dependencies.

Conclusion

  • The Solid Trifecta (Queue, Cache, Cable) eliminates Redis as a hard dependency for jobs, caching, and WebSockets
  • The built-in authentication generator provides a foundation for session-based auth without third-party gems
  • params.expect replaces params.require.permit with stricter, safer parameter handling
  • Propshaft replaces Sprockets as the default asset pipeline, delegating bundling to modern tools
  • Kamal 2 and Thruster deliver zero-downtime deployment without Nginx or Capistrano
  • Upgrading from Rails 7.2 is incremental: update deps, run app:update, adopt new features one at a time
  • Rails 8.1 adds continuable jobs and built-in CI for teams seeking fewer external tools

Start practicing!

Test your knowledge with our interview simulators and technical tests.

Tags

#ruby-on-rails
#rails-8
#tutorial
#migration
#solid-queue
#solid-cache
#authentication

Share

Related articles