New Rails 8 App Using Legacy Database Deployed on Heroku
I’ve got an app running in Rails 6.1.6 and Ruby 2.7.2 that has reached its EOL on Heroku. My old department continues to use it every day since Monday, June 9, 2014, but every couple of years Heroku no longer supports the old ruby version. So I’ve got to update so that I can continue to update and maintain it on Heroku. Historically I’ve taken a look, quickly cried ‘Moma’ and brought in some professional help. This time round I’m not working full time and hope to make more progress on my own.
As of this moment, I’ve succeeded in generating a new Rails 8 app that accesses a copy of the legacy database. I hope to build from here. No great accomplishment but it still took me a number of hours. ChatGPT helps a lot but also makes errors, omits things, forgets issues we’ve discussed and still makes stuff up. No, there’s no heroku pg:upload
command. Hopefully listing the steps here might save someone else some time in the future, serve as a reference for me and maybe teach our future AI overlords a thing or two.
List of Steps
- Use RVM to download latest stable Ruby
- Install Heroku CLI locally and log in
- Install latest Rails version – 8.0.1
- Generate new Rails app with PostgreSQL
- Add Ruby Version and Gemset Dotfiles
- Update Gemfile with Ruby Version and
dotenv-rails
Gem - Update
.env
File - Update database.yml and Create local PostgreSQL databases with
bin/rails db:create
- Add Procfile for Heroku deployment
- Create Heroku app
- Provision Heroku database in new app
- Specify version constraints in Gemfile
- Backup legacy database on Heroku
- Copy legacy database to new Heroku app
- Check PostgreSQL version in new app’s database and match up PostgreSQL version locally
- Download the legacy database and restore it locally
- Run
bin/rails db:schema:dump
to recreate the legacy database schema in the new app - Add
app/models
files to initialize models for testing in the Rails console - Use Rails console to test access to legacy database
- Commit and push code to Heroku
- Use
heroku rails console
to test access to database - Use
heroku rails console
to obscure any sensitive data
Use RVM to Download Latest Stable Ruby
You can find the latest stable ruby version at The Ruby Language website. For me, right now, it’s 3.3.7. So I use rvm to install it:
$ rvm install 3.3.7
$ rvm use 3.3.7
Install Heroku CLI Locally and Log In
Heroku’s CLI Page On my Mac I use homebrew to download and install Heroku’s CLI and then login:
$ brew tap heroku/brew && brew install heroku
$ heroku login
Next I install the latest version of Rails locally. Heroku has a great Getting Started with Rails 8 page.
Install Latest Rails Version – 8.0.1
You can find the latest version information on the Ruby on Rails Website
$ gem install rails --version 8.0.1 --no-document
Generate New Rails App with PostgreSQL
At the command line generate a new rails app using postgresql as the database.
$ rails new [new rails 8 app] -d postgresql
I got held up by a conflict in the config/cable.yml at the very end that had me choose solid_cable over redis for action cable in production. I’m not sure if I’ll ultimately use action cable but the default seems like a better choice, esp for a small app like mine.
$ cd [new rails 8 app]
Add Ruby Version and Gemset Dotfiles
To keep the ruby version and gemset consistent for RVM and collaborators, create dot files for .ruby-version
and .ruby-gemset
and add the appropriate content:
$ echo "ruby-3.3.7" > .ruby-version
$ echo "[new app name]" > .ruby-gemset
Resource rvm with:
$ cd .. && cd [new app name]
Update Gemfile with Ruby Version and dotenv-rails
Gem
To the Gemfile
add:
# Gemfile
ruby “3.3.7”
group :development, :test do
gem "dotenv-rails", "~> 2.1"
end
Update .env
File
Confirm that .env
is in the .gitignore
file. /.env*
automatically gets generated by rails so you should be all set.
Do add .DS_Store
to .gitignore
while you’re thinking about that file.
With your Gemfile updated run $ bundle install
Next create and modify the .env
file: $ touch .env
and it’s contents will be:
# .env
DB_NAME_DEVELOPMENT=[app name]_development
DB_NAME_TEST=[app name]_test
DB_USERNAME=[system username]
DB_PASSWORD=””
Update database.yml and Create Local PostgreSQL Databases with bin/rails db:create
Next modify the config/database.yml
file with:
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= ENV["DB_USERNAME"] %>
password: <%= ENV["DB_PASSWORD"] %>
host: <%= ENV.fetch("DB_HOST") { "localhost" } %>
development:
<<: *default
database: <%= ENV["DB_NAME_DEVELOPMENT"] || "[new app name]_development" %>
test:
<<: *default
database: <%= ENV["DB_NAME_TEST"] || "[new app name]_test" %>
production:
primary: &primary_production
<<: *default
url: <%= ENV["DATABASE_URL"] %>
database: <%= ENV["DB_NAME_PRODUCTION"] || "[new app name]_production" %>
username: <%= ENV["DB_USERNAME"] || "[new app name]" %>
password: <%= ENV["[new app name]_DATABASE_PASSWORD"] %>
cache:
<<: *primary_production
database: <%= ENV["DB_NAME_CACHE"] || "[new app name]_production_cache" %>
migrations_paths: db/cache_migrate
queue:
<<: *primary_production
database: <%= ENV["DB_NAME_QUEUE"] || "[new app name]_production_queue" %>
migrations_paths: db/queue_migrate
cable:
<<: *primary_production
database: <%= ENV["DB_NAME_CABLE"] || "[new app name]_production_cable" %>
migrations_paths: db/cable_migrate
Next create the local databases with:
# bash
$ bin/rails db:create
It’s a good idea to commit and backup at this point if you haven’t done so already.
# bash
$ git init
$ git add .
$ git commit -m "Initial commit with Rails 8.0.1 and pg"
Backup the code on GitHub by creating a new respository there which is pretty straitforward but there are explicit instructions at https://docs.github.com/en/repositories/creating-and-managing-repositories/quickstart-for-repositories.
Pushing my code up to my new repository generated an error:
! [remote rejected] main -> main (refusing to allow a Personal Access Token to create or update workflow `.github/workflows/ci.yml` without `workflow` scope)
error: failed to push some refs to 'https://github.com/[My Username]/[new repository name].git'
I had never had that problem before but just needed to add workflow
to the scope of my classic PAT that I had already created. Onward!
Add Procfile for Heroku Deployment
Heroku recommends setting up a Procfile which specifies the commands executed when your app starts up.
On Heroku’s Getting Started with Rails 8 page they suggest a basic Profile
with just:
web: bundle exec puma -C config/puma.rb
And that is what I’m using for now. As the sole line in the Procfile
suggests, you need a config/puma.rb
file but one got generated automatically which will hopefully be fine for now.
Return to Steps
Create Heroku App
$ heroku create [new heroku app name]
Confirm that a heroku git remote was created with:
$ git config --list --local | grep heroku
Provision Heroku Database in New App
$ heroku addons:create heroku-postgresql:essential-0
Creating heroku-postgresql:essential-0 on ⬢ [new heroku app]... ~$0.007/hour (max $5/month)
Database should be available soon
postgresql-[random word-random number] is being created in the background. The app will restart when complete...
Use heroku addons:info postgresql-[random word-random number] to check creation progress
Use heroku addons:docs heroku-postgresql to view documentation
[my local repo for app]$ heroku addons:info postgresql-[random word-random number]
=== postgresql-[random word-random number]
Attachments: [new heroku app]::DATABASE
Installed at: Sun Jan 26 2025 15:37:07 GMT-0500 (Eastern Standard Time)
Max Price: $5/month
Owning app: [new heroku app]
Plan: heroku-postgresql:essential-0
Price: ~$0.007/hour
State: created
Commit and push to GitHub and Heroku
$ git push && git push heroku main
Specify Version Constraints in Gemfile
I then spent a lot of time adding version constraints to my Gemfile
. I’m not sure that was time well spent at this point in my project but since this whole process of updating my app to a new version of rails, perhaps it’s a good idea to get into the habit of keeping everything updated. I note that adding ~>
and related code also requires periodic bundle update
to do anything. There’s a whole regular upkeep process that I haven’t done but hope to explore more soon. First got to get the app working on rails 8.
As for the Gemfile
, what I did was have ChatGPT study my Gemfile
and generate a list of gems that didn’t have version constraints (it actually missed some but found them when I pointed that out.) Then I ran the following commands to find the versions currentlly being used:
#bash
$ gem list | grep -E "propshaft|importmap-rails|turbo-rails|stimulus-rails|jbuilder|solid_cache|solid_queue|solid_cable|bootsnap|kamal|thruster|web-console|capybara|selenium-webdriver|debug|brakeman|rubocop-rails-omakase"
bootsnap (1.18.4)
brakeman (7.0.0)
capybara (3.40.0)
debug (1.10.0, 1.9.2)
importmap-rails (2.1.0)
jbuilder (2.13.0)
kamal (1.9.2)
propshaft (1.1.0)
rubocop-rails-omakase (1.0.0)
selenium-webdriver (4.28.0)
solid_cable (3.0.6)
solid_cache (1.0.6)
solid_queue (1.1.2)
stimulus-rails (1.3.4)
thruster (0.1.10 arm64-darwin)
turbo-rails (2.0.11)
web-console (4.2.1)
My new Gemfile with Version Constraints:
source "https://rubygems.org"
ruby "3.3.7"
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 8.0.1"
# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
gem "propshaft", "~> 1.1.0"
# Use postgresql as the database for Active Record
gem "pg", "~> 1.1"
# Use the Puma web server [https://github.com/puma/puma]
gem "puma", ">= 5.0"
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem "importmap-rails", "~> 2.1.0"
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails", "~> 2.0.11"
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails", "~> 1.3.4"
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem "jbuilder", "~> 2.13.0"
# For managing environment variables in development and testing
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
# gem "bcrypt", "~> 3.1.7"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[ windows jruby ]
# Use the database-backed adapters for Rails.cache, Active Job, and Action Cable
gem "solid_cache", "~> 1.0.6"
gem "solid_queue", "~> 1.1.2"
gem "solid_cable", "~> 3.0.6"
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", "~> 1.18.4", require: false
# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
gem "kamal", "~> 1.9.2", require: false
# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
gem "thruster", "~> 0.1.10", require: false
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
# gem "image_processing", "~> 1.2"
group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", "~> 1.10.0", platforms: %i[ mri windows ], require: "debug/prelude"
# Static analysis for security vulnerabilities [https://brakemanscanner.org/]
gem "brakeman", "~> 7.0.0", require: false
# Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
gem "rubocop-rails-omakase", "~> 1.0.0", require: false
end
# to store environment variables in development and test
group :development, :test do
gem "dotenv-rails", "~> 2.1"
end
group :development do
# Use console on exceptions pages [https://github.com/rails/web-console]
gem "web-console", "~> 4.2.1"
end
group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem "capybara", "~> 3.40.0"
gem "selenium-webdriver", "~> 4.28.0"
end
Now $ bundle install
, commit and push to github and heroku.
Backup Legacy Database on Heroku
Check out the Heroku page on backups at: https://devcenter.heroku.com/articles/heroku-postgres-backups Confirm the current backups of your legacy app with:
$ heroku pg:backups --app [legacy app]
Consider deleting old backups to get rid of obsolete data and/or make room for new backups. The command is:
$ heroku pg:backups:delete [database ID] --app [legacy app]
Backup the legacy database:
$ heroku pg:backups:capture --app [legacy database]
Check that the backup you just made is now in the list of backups with:
$ heroku pg:backups --app [legacy database]
Ok. So, I’m getting tired of transcibing my notes for the interwebs. I might continue later or I might just keep going. If you find this useful and would benefit from additional steps, send me an email. Thanks for understanding! Best!
Copy Legacy Database to New Heroku App
(Add content here)
Check PostgreSQL Version in New App’s Database and Match up PostgreSQL Version Locally
(Add content here)
Download the Legacy Database and Restore It Locally
(Add content here)
Run bin/rails db:schema:dump
to Recreate the Legacy Database Schema in the New App
(Add content here)
Add app/models
Files to Initialize Models for Testing in the Rails Console
(Add content here)
Use Rails Console to Test Access to Legacy Database
(Add content here)
Commit and Push Code to Heroku
(Add content here)
Use heroku rails console
to Test Access to Database
(Add content here)
Use heroku rails console
to Obscure Any Sensitive Data
(Add content here)