<!-- LLM_VERSION_INFO
FORMAT: text/markdown
CONTENT_TYPE: article
ORIGINAL_URL: https://fly.io/phoenix-files/safe-ecto-migrations
ALTERNATE_VERSION: phoenix-files/safe-ecto-migrations/index.html (text/html)
EXTRACTION_DATE: 2026-04-18T22:26:14.391Z

This is the markdown version with text-only content (images converted to alt-text).
For rich formatting with images, request the HTML version at: phoenix-files/safe-ecto-migrations/index.html
-->

## Author
  
**Name**  
David Bernheisel  
[@bernheisel](https://twitter.com/bernheisel)

Fly.io runs apps close to users, by transmuting Docker containers into micro-VMs that run on our own hardware around the world. This post launches off the [Safe Ecto Migrations](/content/phoenix-files/safe-ecto-migrations/index.html) series guide. If you just want to ship your Phoenix app, the easiest way to learn more is to [try it out](/content/docs/speedrun/index.html); you can be up and running in just a couple of minutes.

As an Elixir developer who cares about system up-time and avoiding “scheduled maintenance” windows, and more importantly avoiding “unscheduled maintenance” windows 😉, this guide dives deep into Ecto database migrations and how they can be used safely in production systems.

This guide comes in a 4 part series of posts. The guide helps you:

1. Understand Ecto migrations  
2. Migrate and rollback the database using Mix releases  
3. Avoid pitfalls during migrations

This guide includes a set of recipes for common migration scenarios. You can jump ahead to those, but it is helpful to understand what’s going on at a deeper level and to get your project setup ahead of time for running migrations in a production environment.

**Note**: This guide uses PostgreSQL and may differ if you’re using a different database. I’ll note where differences may be, but I do not go into depth on different database systems. This was also written using Ecto 3.6.x.

## Guide Table of Contents

- **Part 1:** [Anatomy of an Ecto migration](/content/phoenix-files/anatomy-of-an-ecto-migration/index.html)  
  - [Inspect SQL](/content/phoenix-files/anatomy-of-an-ecto-migration#inspect-sql/index.html)  
  - [Migration Options](/content/phoenix-files/anatomy-of-an-ecto-migration#migration-options/index.html)  
  - [Inspecting Locks In a Query](/content/phoenix-files/anatomy-of-an-ecto-migration#inspecting-locks-in-a-query/index.html)  
  - [Safeguards in the database](/content/phoenix-files/anatomy-of-an-ecto-migration#safeguards-in-the-database/index.html)  
  - [Add a lock_timeout](/content/phoenix-files/anatomy-of-an-ecto-migration#add-a-lock_timeout/index.html)  
  - [Statement Timeout](/content/phoenix-files/anatomy-of-an-ecto-migration#statement-timeout/index.html)  
- **Part 2:** [How to migrate Mix Release projects](/content/phoenix-files/how-to-migrate-mix-release-projects/index.html)  
  - [Release Module](/content/phoenix-files/how-to-migrate-mix-release-projects#release-module/index.html)  
  - [Start the Release](/content/phoenix-files/how-to-migrate-mix-release-projects#start-the-release/index.html)  
  - [Check migration status](/content/phoenix-files/how-to-migrate-mix-release-projects#check-migration-status/index.html)  
  - [Run the migrations](/content/phoenix-files/how-to-migrate-mix-release-projects#run-the-migrations/index.html)  
  - [OMG ROLL IT BACK](/content/phoenix-files/how-to-migrate-mix-release-projects#omg-roll-it-back/index.html)  
- **Part 3:** [Migration Recipes](/content/phoenix-files/migration-recipes/index.html)  
  - [Adding an index](/content/phoenix-files/migration-recipes#adding-an-index/index.html)  
  - [Adding a reference or foreign key](/content/phoenix-files/migration-recipes#adding-a-reference-or-foreign-key-constraint/index.html)  
  - [Adding a column with a default value](/content/phoenix-files/migration-recipes#adding-a-column-with-a-default-value/index.html)  
  - [Changing the type of a column](/content/phoenix-files/migration-recipes#changing-the-type-of-a-column/index.html)  
  - [Removing a column](/content/phoenix-files/migration-recipes#removing-a-column/index.html)  
  - [Renaming a column](/content/phoenix-files/migration-recipes#renaming-a-column/index.html)  
  - [Renaming a table](/content/phoenix-files/migration-recipes#renaming-a-table/index.html)  
  - [Adding a check constraint](/content/phoenix-files/migration-recipes#adding-a-check-constraint/index.html)  
  - [Setting NOT NULL on an existing column](/content/phoenix-files/migration-recipes#setting-not-null-on-an-existing-column/index.html)  
  - [Adding a JSON column](/content/phoenix-files/migration-recipes#adding-a-json-column/index.html)  
  - [References](/content/phoenix-files/migration-recipes#references/index.html)  
  - [Reference Material](/content/phoenix-files/migration-recipes#reference-material/index.html)  
- **Part 4:** [Backfilling Data](/content/phoenix-files/backfilling-data/index.html)  
  - [Bad](/content/phoenix-files/backfilling-data#bad/index.html)  
  - [Good](/content/phoenix-files/backfilling-data#good/index.html)  
  - [Batching Deterministic Data](/content/phoenix-files/backfilling-data#batching-deterministic-data/index.html)  
  - [Batching Arbitrary Data](/content/phoenix-files/backfilling-data#batching-arbitrary-data/index.html)  
  - [We’re Done!](/content/phoenix-files/backfilling-data#were-done/index.html)

# Fly ❤️ Elixir

Fly is an awesome place to run your Elixir apps. It’s really easy to get started. You can be running in minutes.

[Deploy your Elixir app today! →](/content/docs/elixir/index.html)
