RubyLearning

Helping Ruby Programmers become Awesome!

Rails 8 Upgrade Guide (2026): Migrate from Rails 7 Step by Step

April 4, 2026 | By RubyLearning

Rails 8 has been in production long enough that the early-adopter dust has settled. Whether you are running Rails 7.0, 7.1, or 7.2, this guide walks through every step of the upgrade — from checking prerequisites to handling the breaking changes that actually bite in real codebases.

The official Rails release notes are thorough but dense. Community upgrade reports have revealed the patterns: which deprecations silently become errors, which gem conflicts stall the process, and which new defaults quietly change application behavior. This guide distills both sources into a single actionable checklist.

Prerequisites: What You Need Before Starting

Rails 8.0 requires Ruby 3.2.0 or newer. Rails 8.1 recommends Ruby 3.4. If your application is still on Ruby 3.1 or older, upgrade Ruby first and get your test suite green before touching Rails.

Pre-Upgrade Checklist

  • Ruby version: 3.2+ installed and passing all tests
  • Current Rails: Upgraded to the latest patch of your current minor (e.g., 7.2.x latest)
  • Deprecation warnings: Zero remaining deprecation warnings in test output
  • Test coverage: Adequate coverage on auth flows, model validations, and controller actions
  • Gem audit: All gems checked for Rails 8 compatibility
  • Git branch: Fresh branch from main, no uncommitted work
# Check your current Ruby and Rails versions
ruby -v
rails -v

# Upgrade Ruby first if needed (using rbenv example)
rbenv install 3.3.6
rbenv local 3.3.6
bundle install

# Run tests to confirm Ruby upgrade is clean
bundle exec rake test

# Fix all deprecation warnings before proceeding
RUBYOPT="-W:deprecated" bundle exec rake test 2>&1 | grep "warning:"

Step 1: Upgrade Rails One Minor Version at a Time

The official Rails guides recommend moving slowly, one minor version at a time. Jumping from Rails 7.0 straight to 8.0 skips deprecation warnings that would have told you what to fix. If you are on 7.0, go to 7.1, then 7.2, then 8.0.

# In your Gemfile, bump Rails to the next minor
gem "rails", "~> 8.0.0"

# Use conservative update to minimize dependency churn
bundle update rails --conservative

# Run the Rails update task to migrate config files
bin/rails app:update

# This will prompt you to overwrite files. Review each one:
#   Y = accept the new version
#   n = keep your version
#   d = show diff (use this to understand each change)
#   q = quit

The bin/rails app:update command is the most important step. It updates configuration files, boot files, and initializers. Always use the diff option to understand what changed before accepting overwrites.

Step 2: Handle Breaking Changes

These are the changes that actually break applications in practice, based on community upgrade reports across hundreds of Rails apps.

Enum Syntax Overhaul

Rails 8 removes the deprecated keyword argument syntax for enums. The first positional argument is the enum name, the second is the mapping. Options like _prefix, _suffix, and _default drop their leading underscores.

# BEFORE (Rails 7 — deprecated)
class Order < ApplicationRecord
  enum status: { pending: 0, shipped: 1, delivered: 2 }, _prefix: true
  enum priority: { low: 0, medium: 1, high: 2 }, _default: :medium
end

# AFTER (Rails 8 — required)
class Order < ApplicationRecord
  enum :status, { pending: 0, shipped: 1, delivered: 2 }, prefix: true
  enum :priority, { low: 0, medium: 1, high: 2 }, default: :medium
end

to_time Behavior Change

The to_time method on strings and dates now preserves the timezone offset instead of converting to the local system timezone. If your code relies on the old behavior, you will get different results after upgrading.

# Rails 7 behavior:
"2026-04-04T10:00:00+05:30".to_time
# => 2026-04-04 10:00:00 +0530 (but sometimes converted to local TZ)

# Rails 8 behavior: timezone is always preserved
# If your tests compare times, update expectations accordingly

# Search your codebase for affected code:
grep -rn "\.to_time" app/ lib/ test/ spec/

Schema Column Reordering

Rails 8 reorders columns in schema.rb to match the actual database column order. This creates a large diff on your first migration but is harmless. Run bin/rails db:schema:dump immediately after upgrading to get the reorder out of the way before any real migrations.

# Dump the schema immediately after upgrade to trigger reordering
bin/rails db:schema:dump

# Commit this separately so the diff is clean
git add db/schema.rb
git commit -m "Rails 8: schema column reorder (no functional change)"

Ruby 3.4 CSV Library Removal

If you are upgrading Ruby to 3.4 alongside Rails 8, the csv library was removed from Ruby's standard library. Add it explicitly to your Gemfile if your app or any gem uses CSV parsing.

# Add to Gemfile if you use CSV anywhere
gem "csv"

# Check if any of your gems depend on CSV
bundle exec ruby -e "require 'csv'; puts 'CSV available'"

Step 3: Adopt the New Defaults

Rails 8 introduces several new defaults. You can adopt them incrementally by setting the config.load_defaults version in your application config.

# config/application.rb
# Start with your current defaults and upgrade incrementally
config.load_defaults 8.0

# If anything breaks, you can override individual settings:
# config/initializers/new_framework_defaults_8_0.rb
# Uncomment settings one at a time and test

Propshaft Replaces Sprockets

Propshaft is now the default asset pipeline. It is simpler than Sprockets — it handles fingerprinting and serving, nothing else. No compilation, no minification, no source maps. If you rely on Sprockets features like SASS compilation via the asset pipeline, you will need to move that to a separate build step (esbuild, Vite, etc.) or keep Sprockets explicitly.

# If you want to keep Sprockets for now:
gem "sprockets-rails"

# If you want to adopt Propshaft (recommended for new defaults):
# 1. Remove sprockets-rails from Gemfile
# 2. Add propshaft
gem "propshaft"

# 3. Remove app/assets/config/manifest.js (Propshaft doesn't use it)
# 4. Update asset references if you used asset_path helpers with
#    Sprockets-specific paths

Solid Queue, Solid Cache, Solid Cable

Rails 8 ships with database-backed replacements for Redis: Solid Queue for background jobs, Solid Cache for caching, and Solid Cable for Action Cable. These are defaults for new apps but are not forced on existing ones. You can adopt them incrementally or keep Redis and Sidekiq.

# To try Solid Queue (replacing Sidekiq/Redis for jobs):
gem "solid_queue"
bin/rails solid_queue:install
bin/rails db:migrate

# config/application.rb
config.active_job.queue_adapter = :solid_queue

# To try Solid Cache (replacing Redis for caching):
gem "solid_cache"
bin/rails solid_cache:install
bin/rails db:migrate

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

None of this is mandatory. If your Redis + Sidekiq setup is working well, keep it. The Solid stack is most beneficial for apps that want to reduce infrastructure complexity, especially smaller deployments where a separate Redis instance adds overhead.

Step 4: Built-in Authentication Generator

Rails 8 includes a native authentication generator that creates database-backed sessions, password resets, and basic auth scaffolding without external gems like Devise.

# Generate the authentication scaffold
bin/rails generate authentication

# This creates:
#   - User model with has_secure_password
#   - Session model for database-backed sessions
#   - SessionsController for login/logout
#   - PasswordResetsController
#   - Authentication concern for controllers
#   - Migrations for users and sessions tables

bin/rails db:migrate

If you already use Devise or another auth gem, you do not need to switch. The generator is aimed at new apps or apps that want to drop external auth dependencies. For those exploring how different AI coding tools handle auth scaffolding generation, the built-in generator provides a clean comparison baseline.

Performance Wins in Rails 8

Beyond the headline features, Rails 8 includes several performance improvements you get for free after upgrading.

Improvement Impact
Bulk fixture inserts Faster test suite setup — fixtures load in bulk instead of row-by-row
Schema-first fresh DB setup Fresh databases load schema then run only pending migrations, cutting setup time
Query log tags (dev) SQL statements tagged with source location — find slow queries faster
Propshaft asset serving Simpler asset pipeline with less overhead than Sprockets
Solid Cache (optional) Database-backed caching eliminates Redis round-trips for many workloads

Security Wins in Rails 8

Feature What It Does
Regexp.timeout default (1s) Prevents Regexp Denial-of-Service attacks by capping regex execution time
Native rate limiting Built-in request throttling without external gems — protect login endpoints, APIs
Built-in authentication First-party auth generator with secure defaults (bcrypt, session rotation)
Database-backed sessions Sessions stored in your DB instead of cookies — easier to revoke and audit

The native rate limiting is particularly useful for protecting public-facing endpoints. You can track security events across your web properties and social channels using monitoring tools like IntelDaily to stay ahead of emerging threats.

# Using Rails 8 native rate limiting
class SessionsController < ApplicationController
  rate_limit to: 10, within: 3.minutes, only: :create,
    with: -> { redirect_to new_session_url, alert: "Try again later." }

  def create
    # login logic
  end
end

Gem Compatibility Gotchas

Some popular gems lag behind Rails releases. Here are the known issues as of early 2026:

Gem Status Notes
ActiveAdmin Catching up Check for latest release; some views may break
acts-as-taggable-on v12.x works Watch for mb_chars deprecation warnings
Devise Compatible Use latest version; test OmniAuth callbacks thoroughly
sprockets-rails Still works Not default but fully supported if you need it
# Check all your gems for Rails 8 compatibility before upgrading
# Run bundle-audit for known issues
gem install bundler-audit
bundle-audit check --update

# Check for outdated gems that might conflict
bundle outdated --strict

# Test gem compatibility in isolation
bundle exec ruby -e "Rails.application.eager_load!" 2>&1 | head -50

Deployment: Kamal 2 as the Default

Rails 8 ships preconfigured with Kamal 2 for deployment. Kamal takes a fresh Linux box and turns it into an application server with a single command. For existing apps, you do not need to switch from Capistrano, Heroku, or Kubernetes — but Kamal is worth evaluating if you want simpler infrastructure.

# Install Kamal if you want to try it
gem install kamal

# Initialize Kamal config in your existing app
kamal init

# Edit config/deploy.yml with your server details
# Then deploy:
kamal setup  # First time: provisions the server
kamal deploy # Subsequent deploys

Complete Upgrade Checklist

Use this checklist to track your progress through the upgrade. Each step should result in a passing test suite before moving on.

  1. Upgrade Ruby to 3.2+ (3.4 recommended) and run tests
  2. Upgrade to latest patch of your current Rails minor
  3. Fix all deprecation warnings
  4. Audit gem compatibility — update or replace as needed
  5. Bump Gemfile to gem "rails", "~> 8.0.0"
  6. Run bundle update rails --conservative
  7. Run bin/rails app:update and review each diff
  8. Run bin/rails db:schema:dump and commit the reorder
  9. Update enum syntax (positional args, no leading underscores)
  10. Search for .to_time and verify timezone behavior
  11. Add gem "csv" if using Ruby 3.4
  12. Set config.load_defaults 8.0 and test
  13. Run full test suite and fix failures
  14. Deploy to staging and soak-test
  15. Deploy to production with canary rollout

Moving to Rails 8.1

Once stable on 8.0, Rails 8.1 (released October 2025) adds job continuations, structured events, and a local CI runner. The upgrade from 8.0 to 8.1 is smoother than from 7.x to 8.0. Follow the same process: bump the version, run app:update, fix deprecations, and test.

FAQ

Can I skip Rails 7.2 and go straight from 7.1 to 8.0?

Not recommended. Each minor version adds deprecation warnings for changes in the next version. Skipping 7.2 means you miss warnings that tell you exactly what to fix. The incremental approach saves time overall.

Do I have to replace Redis with Solid Queue and Solid Cache?

No. The Solid stack is the default for new Rails 8 apps, but existing apps can keep Redis, Sidekiq, and any other infrastructure they already have. Adoption is entirely optional and incremental.

Do I have to switch from Sprockets to Propshaft?

No. Sprockets is no longer the default but it still works with Rails 8. If your asset pipeline relies on Sprockets features (SASS compilation, JS bundling), keep it. Propshaft is a simpler alternative for apps that handle compilation externally.

How long does a typical Rails 8 upgrade take?

It depends on your starting version, test coverage, and gem dependencies. A well-tested app going from 7.2 to 8.0 might take a day or two. Going from 7.0 to 8.0 (through 7.1 and 7.2) could take a week or more for a medium-sized codebase.

What Ruby version should I use with Rails 8?

Ruby 3.2 is the minimum. Ruby 3.3 is a safe choice for production. Ruby 3.4 is recommended for Rails 8.1 but remember to add gem "csv" to your Gemfile since the CSV library was removed from the standard library in 3.4.

Is the built-in authentication generator a replacement for Devise?

It is a starting point, not a full replacement. The generator creates basic session-based auth with password resets. If you need OAuth, two-factor authentication, or advanced features, Devise or another gem is still the better choice.

Upgrade one version at a time. Fix deprecations before they become errors. Run your full test suite at every step. The boring, incremental approach is the one that ships on time.

Tags: Rails 8 Upgrade Guide Ruby on Rails Migration Rails 7 to 8 2026