Guides / How to roll back a Django deploy (and bad migrations)

How to roll back a Django deploy (and bad migrations)

A deploy went wrong and you need to go back. Rolling back Django code is easy. Rolling back the database is where people get hurt, because migrations don't undo themselves when you redeploy old code. This guide covers both halves, in the order to do them.

What it means

A rollback has two independent parts: the code (and its settings) and the database schema. Redeploying the previous version fixes the code. If the bad release also ran a migration, the old code now runs against a newer schema, and whether that works depends on what the migration did.

Why it goes wrong

  1. There is no release history. You deploy by pulling main on a server, so "the previous version" is whatever you can reconstruct from git.
  2. The old code can't run on the new schema. The migration dropped or renamed a column the old code still reads.
  3. The migration can't be reversed. A RunPython without a reverse function, or a data change that lost information.
  4. The settings changed too. The release also needed a new environment variable, and removing it breaks the old code.

The fix

1. Roll back the code

Never rewrite history on main. Revert the bad change, or redeploy a known-good tag:

git revert --no-edit <bad-commit>     # or: git revert -m 1 <merge-commit>
git push
# or deploy an earlier tag / image directly:
git checkout v1.4.2

2. Decide whether the database needs to go back

Often it doesn't. If the migration only added a nullable column or a new table, the old code ignores it and runs fine. Leave the schema alone. If the migration removed or renamed something the old code uses, reverse it:

python manage.py showmigrations myapp
python manage.py sqlmigrate myapp 0013 --backwards   # review the SQL first
python manage.py migrate myapp 0012                   # go back to just before 0013

Run the reverse migration with the new code still present (the migration file for 0013 must exist), and only then switch to the old code. Take a database backup or snapshot before you do.

3. When Django says a migration is irreversible

django.db.migrations.exceptions.IrreversibleError means a RunPython or RunSQL has no reverse. Prevent it when you write the migration:

from django.db import migrations


def forwards(apps, schema_editor):
    ...


class Migration(migrations.Migration):
    dependencies = [("myapp", "0012_previous")]
    operations = [
        migrations.RunPython(forwards, migrations.RunPython.noop),  # reversible, does nothing on the way back
    ]

If it is already deployed, you restore from a backup, or write the reverse by hand as a new migration. There is no shortcut.

Making it stick in production

See also unapplied migrations in production for the opposite problem: code that is ahead of the schema.

Or skip it

Django Cloud saves every deploy as a numbered release you can roll back to in one click, or with djangocloud rollback. It brings back that release's settings, and warns you when an older release would run against a newer database.

pip install djangocloud-cli
djangocloud deploy

Get startedSee pricing

Free plans are available for students, AWS Activate startups, open-source, non-profit and research projects. See how Django Cloud compares with other ways to host Django.

Django is a registered trademark of the Django Software Foundation. Django Cloud is not affiliated with or endorsed by it. Found a mistake in this guide? Tell us and we will fix it.