If you are on Rails 7.2 and staring at Rails 8, you are in the happiest version of this migration. 7.2 was specifically shaped as the on-ramp to 8. Most of the hard work - Zeitwerk defaults, Trilogy, async query support, the new framework defaults - already happened when you got to 7.2.

So Rails 7.2 to 8 is usually a short, boring upgrade. If you sequence it right and do not try to also adopt every Rails 8 feature in the same PR.

This is the focused step-by-step. If you are on Rails 7.0 or 7.1, start with the broader Rails 7 to Rails 8 upgrade guide and come back to this when you are on 7.2.

Pre-Flight Checklist

Do not start until all of these are true:

If any of those is false, fix it first. Upgrading Rails on a shaky base is the number-one reason “simple upgrades” become emergencies.

Step 1: Create a Dedicated Upgrade Branch

git checkout -b upgrade/rails-8

Keep this branch narrow. Only the bump and its immediate fallout. New features, refactors, and opportunistic cleanups belong in other PRs.

Step 2: Bump Ruby (If Needed)

Rails 8 requires Ruby 3.2+. Most 7.2 teams are on 3.2 or 3.3 already, but verify:

ruby -v
cat .ruby-version

If you are on 3.1 or earlier, bump Ruby first as a separate PR and deploy. Do not combine Ruby and Rails bumps in one project.

# .ruby-version
3.3.6
# Dockerfile (if applicable)
FROM ruby:3.3.6-slim

Ship Ruby first. Let it bake. Then come back here.

Step 3: Bump Rails

# Gemfile
gem "rails", "~> 8.0.0"
bundle update rails

Expect bundle update rails to surface a handful of gems that pin to < 8.0. Common culprits (check each against current gem versions - this list ages):

For each, check the gem’s changelog. Usually a newer version supports Rails 8. Bump it. If a gem has genuinely not updated, evaluate whether it is still maintained - that is a separate signal worth acting on.

Step 4: Run rails app:update

bundle exec rails app:update

This is interactive. Read every diff. For a Rails 7.2 starting point, the changes are fairly small compared to jumping from 7.0. Expect prompts for:

Do not accept everything blindly. In particular, load_defaults 8.0 flips a number of subtle behaviors. The standard pattern is:

# config/application.rb
config.load_defaults 7.2

And keep a populated config/initializers/new_framework_defaults_8_0.rb. Flip each default intentionally, one PR at a time, after the bump ships. Then eventually bump to config.load_defaults 8.0 and delete the initializer.

Step 5: Review Critical Config

config/database.yml

Rails 8 adopts new defaults for connection pooling and prepared statements. If your database.yml is explicit, you may need to add or adjust:

production:
  <<: *default
  database: myapp_production
  # prepared_statements defaults changed in Rails 8 - verify

config/cache.yml (new)

Only matters if you adopt Solid Cache. For the upgrade itself, leave your existing cache store alone.

config/queue.yml (new)

Same - only matters if you adopt Solid Queue. Keep Sidekiq / GoodJob exactly as-is for the upgrade.

config/puma.rb

Rails 8’s default Puma config is slightly different. If you customized yours, diff the new generator output against your existing file and merge deliberately.

Step 6: Run the Tests

bundle exec rspec

Expect some breakage. The common categories from 7.2 to 8:

Deprecation warnings turned errors

Rails 7.2 deprecations are Rails 8 removals. If your app had warnings like “X will be removed in Rails 8” and you ignored them, now is when they fail. Search the commits for DEPRECATION WARNING in staging logs over the past year.

ActiveRecord::Base.connection usage

Rails 8 prefers ActiveRecord::Base.lease_connection (or role-aware handlers) for some cases. Audit lib/ and app/ for bare .connection calls that bypass the connection pool.

find_or_create_by semantics

Rails 8 tightens behavior around unique constraint conflicts. If you have high-concurrency find_or_create_by calls, review them for new edge cases.

Zeitwerk strictness

Already the default in 7.2, but Rails 8 enforces more aggressively at boot. Run:

bundle exec rails runner "Rails.application.eager_load!"

Fix any autoload errors before deploying.

System specs / browser specs

Re-run browser-based tests with the latest Capybara and Selenium. Rails 8 doesn’t change browser behavior, but upgrade-adjacent gem bumps often do.

Step 7: Boot It Locally, Click Around

Automated tests miss things. Especially:

Walk through these manually before merging. This is 20 minutes that saves 2 hours of staging debugging.

Step 8: Deploy to Staging

Ship the branch to a real staging environment. Keep it there at least a few days. Watch:

If something is odd, it is far cheaper to find it in staging than after production rollout.

Step 9: Production Rollout

Options, roughly safest to riskiest:

  1. Canary / one-instance rollout: deploy to one app instance, watch metrics, then roll to the rest.
  2. Off-peak deploy: Tuesday or Wednesday, middle of the business day so humans are awake.
  3. Full deploy with fast rollback: just ensure your deploy tool can redeploy the previous image in under 5 minutes.

Have a rollback image identified before you deploy. A Rails major upgrade rollback is a container / Gemfile.lock rollback, not a git revert. Your platform needs to support that cleanly.

Step 10: Let It Bake, Then Adopt Rails 8 Features

After the upgrade is stable in production - usually 1-2 weeks - then decide feature by feature:

Solid Cache

Worth it if you are currently paying for Redis primarily for cache. Migrate gradually - keep Redis for Action Cable or Sidekiq if you use them, and move only the Rails.cache store to Solid Cache first.

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

Deploy this as its own PR, with a rollback plan to your previous cache store.

Solid Queue

Only consider if you want to drop Redis-based job backends. Benchmark on your real workload before committing - Sidekiq is hard to beat on throughput for some patterns.

Built-in authentication

Almost never worth swapping a working Devise setup for the built-in generator. New apps, yes. Existing apps, no.

Propshaft

If you are still on Sprockets and it works, leave it. Migrating to Propshaft is a separate project with its own surprises (custom helpers, asset path assumptions, CDN caching).

Flipping load_defaults 8.0

Once all individual defaults in new_framework_defaults_8_0.rb have been flipped and tested, remove the initializer and set config.load_defaults 8.0 in config/application.rb. This is the final step of the upgrade, sometimes weeks after the Gemfile bump.

Specific Rails 7.2 → 8 Gotchas

Beyond the general 7-to-8 gotchas, a few things specifically surprise 7.2 teams:

You might already be on Trilogy

Rails 7.2 made Trilogy a first-class MySQL adapter. If you opted in on 7.2, Rails 8 is mostly invisible here. If you are still on mysql2, Rails 8 is a good time to evaluate - but not mandatory.

Async queries

7.2 introduced more async query paths. Rails 8 expands on them. Nothing breaks, but if you adopted load_async in 7.2, retest behavior under load on 8.

config.active_record.raise_on_assign_to_attr_readonly

Became stricter. If you relied on silent ignoring of writes to readonly attributes, you’ll get raises now.

Rubocop / standard

Not a Rails issue, but 7.2 apps on older Rubocop versions sometimes fail the upgrade CI run. Bump your linter versions before debugging mysterious failures.

Estimated Timeline

A well-maintained Rails 7.2 app that followed the pre-flight checklist:

If you are hitting weeks of pain on the upgrade PR itself, stop. Something in the pre-flight checklist was not actually done. Fix that, then resume.


Want expert eyes on your Rails 7.2 to 8 upgrade? Rails Upgrade Express runs these migrations end to end, a Rails technical audit gives you a realistic scope before you commit, and a Rails Care Plan keeps you at the latest version so the next upgrade is an afternoon, not a project.

Schedule a consultation or email to talk through your Rails 7.2 to 8 upgrade.