SwapSQL

Migrate a Rails app from MySQL to PostgreSQL

PostgreSQL is the database most Rails teams reach for in production — Heroku, Fly and most managed hosts default to it, and ActiveRecord exposes more of its features (jsonb, array columns, partial indexes). The code change is small; the data move is where the care goes. Here is the order that keeps it boring.

1. Swap the database gem

In your Gemfile, replace the MySQL driver with the PostgreSQL one:

# remove
gem "mysql2"
# add
gem "pg"

Then install it:

bundle install

2. Rewrite config/database.yml

Point the adapter at postgresql. PostgreSQL connects over TCP on port 5432 (or a Unix socket); there is no socket: path like MySQL's:

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: 127.0.0.1
  username: myapp
  password: <%= ENV["DATABASE_PASSWORD"] %>

development:
  <<: *default
  database: myapp_development

production:
  <<: *default
  database: myapp_production

Drop any MySQL-only keys you were carrying — collation, charset and strict have no PostgreSQL equivalent and will raise on boot if left behind. PostgreSQL is UTF-8 throughout and strict by default.

3. Know what ActiveRecord hides — and what it doesn't

The migrations are portable, but a few behaviours change under the new adapter:

4. Move the data

Schema only / fresh start. Because db/schema.rb is database-agnostic, you can recreate an empty PostgreSQL database from it directly:

RAILS_ENV=production rails db:create db:schema:load

Real data. There is no Rails command that copies rows across engines. Take a SQL dump from MySQL and convert the dialect — type mappings, AUTO_INCREMENT → sequences, backtick quoting and all:

mysqldump --single-transaction --no-tablespaces \
  -u root -p myapp_production > myapp.sql

Convert myapp.sql to PostgreSQL, then load it with psql -d myapp_production -f converted.sql. This brings schema and data together and preserves your existing IDs and foreign keys.

5. Reset every sequence

After importing rows with explicit primary keys, realign each table's sequence so new inserts get fresh IDs. Run this once in the Rails console (or a one-off rake task):

ActiveRecord::Base.connection.tables.each do |table|
  ActiveRecord::Base.connection.reset_pk_sequence!(table)
end

6. Verify before cutover

Run your full test suite against PostgreSQL, then spot-check row counts on your busiest models (User.count, Order.count) against the MySQL originals. Confirm rails db:migrate:status shows everything up, and exercise the case-sensitive paths (login, search) before pointing production at the new database.

Skip the dialect headaches — convert your MySQL dump to PostgreSQL now →