By Dharin Rajgor,
Last Modified: May 14, 2025
Data Migration in Rails without Downtime
Data Migration in Rails without Downtime

When updating a database schema in a Ruby on Rails application, zero-downtime migrations ensure that database changes do not cause application downtime or errors. Here are key techniques to achieve zero-downtime migrations:

1. Add New Columns Without Removing Old Ones

  • Why? If a column is removed, existing code referencing it will break.
  • How? First, add the new column. Then, update the application to use the new column before removing the old one.
				
					class AddNewColumnToUsers < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :new_column, :string
  end
end

				
			

2. Use add_column With Defaults Carefully

  • Adding a column with a default and NOT NULL constraint locks the table in some databases (e.g., Postgres).
  • Instead, add the column without constraints first and then update records in batches.
				
					class AddNewFieldToUsers < ActiveRecord::Migration[6.1]
    def change
        add_column :users, :new_field, :string, null: true
    end
end
				
			

Then, backfill the data asynchronously:

				
					User.update_all(new_field: 'default_value')
				
			

Finally, apply constraints safely:

				
					class AddNotNullConstraintToUsers < ActiveRecord::Migration[6.1]
    def change
        change_column_null :users, :new_field, false
    end
end
				
			

3. Indexing Without Downtime

  • Adding an index locks the table, making inserts and updates slow.
  • Use the algorithm: :concurrently option (PostgreSQL only).
				
					class AddIndexToUsers < ActiveRecord::Migration[6.1]
  disable_ddl_transaction!

  def change
    add_index :users, :email, unique: true, algorithm: :concurrently
  end
end

				
			

4. Renaming Columns or Tables Safely

  • Rails’ rename_column locks the table. Instead:
    1. Add a new column.
    2. Copy data to the new column.
    3. Update application code to use the new column.
    4. Drop the old column later.

5. Removing Columns Without Breaking Queries

  • Old application instances may still reference a column.
  • Instead, follow a three-step process:
    1. Stop using the column in the application.
    2. Deploy the change.
    3. Remove the column in a separate migration.

6. Dropping Tables Safely

  • Ensure no application code references the table before dropping it.
				
					class DropOldTable < ActiveRecord::Migration[6.1]
  def up
    drop_table :old_table
  end

  def down
    # Optional rollback (recreate table)
  end
end

				
			

7. Avoid Locking Large Tables

  • For large tables, break changes into smaller, incremental migrations.
  • Avoid change_column on large tables, as it locks the entire table.

Best Practices

  • Use disable_ddl_transaction! for index migrations.
  • Deploy migrations separately from application code changes.
  • Backfill data in batches to avoid database overload.
  • Test migrations on a staging environment before production.

Would you like help structuring your migrations for a specific use case? 🚀

Discover the power of Ruby on Rails

for your projects!

Share On: