Question
Rails Database Tasks Explained: db:migrate vs db:reset vs db:schema:load
Question
In Rails, I understand the difference between rake db:migrate and rake db:reset, but I am not sure how rake db:schema:load differs from them.
Here is my current understanding:
rake db:migrate
Runs any migrations that have not been run yet.
rake db:reset
Clears the database and rebuilds it from scratch, which I assume is similar to:
rake db:drop db:create db:migrate
How is rake db:schema:load different from these two tasks, and when should it be used?
Short Answer
By the end of this page, you will understand what each Rails database task does, how they differ, and when to use each one. You will also learn why db:schema:load is usually faster than replaying every migration, and why db:reset does not simply rerun all migrations in the way many beginners expect.
Concept
Rails provides several database tasks because developers need to manage databases in different situations.
The three tasks in this question solve different problems:
db:migrateupdates an existing database by applying only the migrations that have not run yet.db:schema:loadcreates the database structure directly from the current schema file.db:resetdrops the database, recreates it, and then loads the current schema and seeds.
What db:migrate does
When you run:
rake db:migrate
Rails looks at your migration files and checks which ones have already been applied by reading the schema_migrations table in the database.
It then runs only the missing migrations.
This is useful when:
- you add a new migration
- your app is already running
- you want to evolve the database safely over time
What db:schema:load does
When you run:
rake db:schema:load
Rails does not run your migration files one by one.
Instead, it reads the schema file:
Mental Model
Think of your database like a house.
db:migrateis like applying renovation instructions one by one: add a wall, remove a window, paint a room.db:schema:loadis like opening the latest blueprint and building the house exactly as it should look today.db:resetis like bulldozing the house, rebuilding it from the latest blueprint, and then furnishing it with starter items from seeds.
This is why db:schema:load is faster on a fresh database: it skips the entire renovation history and just uses the final design.
Another way to think about it:
- migrations = step-by-step journal
- schema = snapshot
- reset = destroy everything and rebuild from the snapshot
Syntax and Examples
Core commands
rake db:migrate
rake db:schema:load
rake db:reset
In newer Rails versions, you will often see bin/rails instead of rake:
bin/rails db:migrate
bin/rails db:schema:load
bin/rails db:reset
Example migration
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
If this migration has not been run yet, db:migrate will create the users table.
After migrations run, Rails updates db/schema.rb to reflect the current structure.
Example schema file
A simplified db/schema.rb might look like this:
Step by Step Execution
Consider this simplified project state:
- Migration 1 creates
users - Migration 2 adds
emailtousers - Migration 3 creates
posts db/schema.rbalready reflects all of these changes
Using db:migrate
rake db:migrate
Step by step:
- Rails checks the
schema_migrationstable. - It sees which migration versions have already run.
- It executes only the missing migration files.
- It updates the database structure.
- It updates
db/schema.rb.
If only Migration 3 is missing, Rails runs only Migration 3.
Using db:schema:load
rake db:schema:load
Step by step:
- Rails opens
db/schema.rb. - It reads the current database structure definition.
- It creates tables, columns, and indexes directly from that file.
Real World Use Cases
When to use db:migrate
Use it when you are changing an existing application database.
Examples:
- adding a new column like
statusto anorderstable - creating a new table for
comments - adding an index to improve query performance
- deploying a new app version with database changes
When to use db:schema:load
Use it when you need a fresh database with the current structure quickly.
Examples:
- setting up a new developer machine
- creating test databases in CI pipelines
- rebuilding a local database after corruption
- loading the latest schema without replaying years of old migrations
When to use db:reset
Use it when you want a complete clean start locally.
Examples:
- your development data is messy and you want to start over
- seed data has changed and you want to reload everything
- you want to recreate the database exactly as the app currently expects
Common practical rule
- existing database with real data ->
db:migrate - brand new empty database -> often
db:schema:load
Real Codebase Usage
In real Rails projects, developers usually treat these tasks as part of different workflows.
Common team patterns
Day-to-day development
Most of the time, developers run:
bin/rails db:migrate
This keeps their local database in sync with newly added migrations.
New machine setup
A fresh clone of the project often uses schema loading because it is faster than replaying every migration since the beginning of the app.
Test and CI environments
Automated environments often prefer loading the schema because:
- it is faster
- it is more predictable for a clean database
- it avoids failures caused by old migration code that no longer works well with current models
Important pattern: migrations should not depend on current app code
In real codebases, old migrations can break if they rely on application models that have changed.
For example, this can be risky inside a migration:
class BackfillUserNames < ActiveRecord::Migration[7.0]
def up
User.find_each do |user|
user.update!(name: user.email.split('@').first)
end
end
Common Mistakes
Mistake 1: Thinking db:reset runs every migration
Many beginners assume:
rake db:reset
is equivalent to:
rake db:drop db:create db:migrate
Usually, the closer mental model is:
rake db:drop db:create db:schema:load db:seed
How to avoid this mistake:
- remember that reset rebuilds from the current schema snapshot
- do not rely on it to test your full migration history
Mistake 2: Using db:reset when you only need db:migrate
Broken workflow:
# unnecessary and destructive
rake db:reset
If you only added a migration, this destroys all local data.
Better:
rake db:migrate
Mistake 3: Assuming db:schema:load preserves data
It is for building structure, not preserving existing data in a working database.
If you need to update an existing database safely, use migrations.
Comparisons
Quick comparison table
| Task | What it uses | Best for | Keeps existing data? | Runs old migrations? |
|---|---|---|---|---|
db:migrate | Migration files | Updating an existing database | Usually yes | Only pending ones |
db:schema:load | db/schema.rb or db/structure.sql | Building a fresh database quickly | No, typically used for fresh setup | No |
db:reset | Drop/create + schema load + seeds | Rebuilding from scratch | No | No |
db:migrate vs
Cheat Sheet
# Apply only pending migrations
bin/rails db:migrate
# Build database structure from current schema file
bin/rails db:schema:load
# Drop, recreate, load schema, and seed
bin/rails db:reset
Remember
db:migrate= incremental changesdb:schema:load= load current structure snapshotdb:reset= rebuild from scratch
Important details
db:migratechecksschema_migrationsdb:schema:loaddoes not replay migration historydb:resetis closer todb:drop db:create db:schema:load db:seeddb:schema:loadis often faster for fresh databasesdb:migrateis safer for existing databases with real data
Edge cases
- If old migrations are broken,
db:schema:loadmay still work for fresh setup - If your project uses database-specific SQL features, it may use
db/structure.sqlinstead of
FAQ
Does db:reset run migrations in Rails?
Not in the usual step-by-step sense. It typically rebuilds the database from the current schema and then runs seeds.
Is db:schema:load faster than db:migrate?
For a fresh database, usually yes. It skips the full migration history and loads the final structure directly.
When should I use db:migrate instead of db:schema:load?
Use db:migrate when you already have a database with data and want to apply new changes safely.
Does db:schema:load insert seed data?
No. Schema loading creates structure, not starter records.
Can db:reset delete my data?
Yes. It drops the database and recreates it, so existing data is removed.
Why do Rails apps keep both migrations and a schema file?
Migrations record history, while the schema file represents the current database structure.
What if old migrations fail on a fresh setup?
That is one reason teams often use schema loading for fresh databases instead of replaying every old migration.
Should I use these commands in production?
db:migrate is common in production. Destructive tasks like should be used very carefully and usually not on production databases.
Mini Project
Description
Build a tiny Rails-style database workflow example to practice choosing the right command. The project simulates a team working on a blog app with users and posts, where you need to decide whether to migrate an existing database, load a fresh schema, or completely reset the database with seeds.
Goal
Understand which Rails database task to use in three common situations: updating an existing database, creating a fresh database quickly, and rebuilding a local database from scratch.
Requirements
- Create two example migration files: one for
usersand one forposts. - Add a simple
db/schema.rbthat reflects both tables. - Add a
db/seeds.rbfile that inserts at least one user. - Explain which command you would run for an existing database with new changes.
- Explain which command you would run for a brand new database.
- Explain which command you would run when you want to wipe local data and start over.
Keep learning
Related questions
How to Call Shell Commands from Ruby and Capture Output
Learn how to run shell commands in Ruby, capture output, check exit status, and choose the right method for scripts and apps.
How to Check Whether a String Contains a Substring in Ruby
Learn how to check if a string contains a substring in Ruby using include?, match, and multiline string examples.
How to Check if a Hash Key Exists in Ruby
Learn how to check whether a specific key exists in a Ruby hash using key?, has_key?, and include? with clear examples.