{"id":8109,"library":"django-pg-zero-downtime-migrations","title":"Django PG Zero Downtime Migrations","description":"Django postgresql backend that applies migrations with respect to database locks, enabling zero downtime during schema changes. It modifies the standard Django PostgreSQL backend to minimize locks during schema and `RunSQL` operations, helping to ensure continuous application availability during deployments. The library is actively maintained, with frequent releases to support new Django, Python, and PostgreSQL versions, and is currently at version 0.19.","status":"active","version":"0.19","language":"en","source_language":"en","source_url":"https://github.com/tbicr/django-pg-zero-downtime-migrations","tags":["django","postgresql","migrations","zero-downtime","database","deployment"],"install":[{"cmd":"pip install django-pg-zero-downtime-migrations","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"This is a Django backend; specific Django versions are supported per library release.","package":"Django","optional":false},{"reason":"This library is a PostgreSQL-specific backend; specific PostgreSQL versions are supported per library release.","package":"PostgreSQL","optional":false}],"imports":[{"note":"The core usage involves replacing the default PostgreSQL engine with the provided zero-downtime backend in Django settings.","wrong":"'ENGINE': 'django.db.backends.postgresql'","symbol":"DATABASES['default']['ENGINE']","correct":"DATABASES = {\n    'default': {\n        'ENGINE': 'django_pg_zero_downtime_migrations.backends.postgres',\n        ...\n    }\n}"}],"quickstart":{"code":"import os\n\nDATABASES = {\n    'default': {\n        'ENGINE': 'django_pg_zero_downtime_migrations.backends.postgres',\n        'NAME': os.environ.get('DB_NAME', 'your_database_name'),\n        'USER': os.environ.get('DB_USER', 'your_user'),\n        'PASSWORD': os.environ.get('DB_PASSWORD', 'your_password'),\n        'HOST': os.environ.get('DB_HOST', 'localhost'),\n        'PORT': os.environ.get('DB_PORT', '5432'),\n    }\n}\n\n# Recommended settings for zero-downtime operations\nZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT = '2s'\nZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT = '2s'\nZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT = True\nZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE = True\n\n# For Django < 5.0 when adding columns with code defaults\n# ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT = True","lang":"python","description":"To enable zero-downtime migrations, configure your Django `DATABASES` setting to use the library's PostgreSQL backend. Additionally, it's highly recommended to set specific timeouts and enable raising errors for unsafe operations to enforce zero-downtime principles."},"warnings":[{"fix":"Review the release notes for your target `django-pg-zero-downtime-migrations` version to ensure compatibility with your environment. Upgrade Django, Python, or PostgreSQL as needed, or pin the library to a compatible older version.","message":"This library frequently drops support for older Django, Python, and PostgreSQL versions. For example, version 0.17 dropped Django 3.2, 4.0, 4.1 and Python 3.6, 3.7. Version 0.14 dropped PostgreSQL 11 and the setting `ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL`.","severity":"breaking","affected_versions":"0.12, 0.13, 0.14, 0.17 onwards"},{"fix":"Remove any calls to this command. The library now handles `NOT NULL` constraint creation and validation internally as part of its zero-downtime strategy using `CHECK` constraints.","message":"The `migrate_isnotnull_check_constraints` command was marked deprecated in 0.14 and completely dropped in 0.17.","severity":"deprecated","affected_versions":"0.14 (deprecated), 0.17 (removed)"},{"fix":"Keep individual migration files small and focused. Thoroughly test migrations in a staging environment that mirrors production. Always have a database backup and a rollback plan. Monitor migrations closely during deployment.","message":"The backend does not use transactions for schema migrations (except for `RunPython` operations) to avoid deadlocks for complex migrations. If a schema migration fails mid-operation, the database state might be inconsistent, requiring manual intervention.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For Django < 5.0, consider setting `ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT = True` in your settings. For maximum safety, add new columns as nullable first, deploy code that populates the column for existing data, and then create a subsequent migration to set the column as `NOT NULL`.","message":"Adding a `NOT NULL` column with a code-level default (e.g., `default=...` in a Django model field) can still cause downtime issues in older Django versions. PostgreSQL will attempt to fill existing rows, acquiring a table lock. While the library makes `ADD COLUMN DEFAULT NOT NULL` safe for Django 5.0+ with `db_default` and offers `ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT` for older Django versions, manual steps are safer if not properly configured.","severity":"gotcha","affected_versions":"Prior to 0.16, and Django versions before 5.0 without `db_default`."}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"When adding a `NOT NULL` column: (1) Add the column as nullable. (2) Deploy code that handles the new nullable field and populates it for existing data (e.g., via a `RunPython` operation). (3) Deploy a subsequent migration to set the column as `NOT NULL`. For Django 5.0+, use `db_default` when adding `NOT NULL` fields to ensure a database-level default. For older Django, consider `ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT = True` or the multi-step approach.","cause":"A `NOT NULL` field was added without a `db_default` (Django 5.0+) or sufficient handling for existing rows, causing old application code (running during a rolling deployment) to fail when creating/updating records without providing a value for the new field.","error":"django.db.utils.ProgrammingError: column \"new_field\" contains null values"},{"fix":"Increase `ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT` and/or `ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT` in your Django `settings.py` for operations that are genuinely long but safe. Alternatively, analyze the specific migration step and break it down into smaller, inherently safer operations (e.g., creating indexes concurrently, adding nullable columns before setting `NOT NULL`).","cause":"A migration operation acquired a lock on a table or took too long to execute, exceeding PostgreSQL's configured `statement_timeout` or `lock_timeout` settings. This can happen with complex schema changes or large tables.","error":"django.db.utils.OperationalError: canceling statement due to statement timeout"},{"fix":"Implement a multi-step rename strategy. First, add the new field/model (deploy code to use the new while tolerating the old). Second, backfill data if necessary. Third, deploy code that solely uses the new field/model. Finally, remove the old field/model in a separate migration. The library's `SeparateDatabaseAndState` operation can assist with certain renaming scenarios using views.","cause":"Renaming a model or field in a single migration step causes a conflict with old application code that is still running during a rolling deployment and references the old name.","error":"django.db.utils.ProgrammingError: relation \"old_table_name\" does not exist"},{"fix":"Ensure the library is installed with `pip install django-pg-zero-downtime-migrations`. Verify the `ENGINE` path in `settings.py` is exactly `'django_pg_zero_downtime_migrations.backends.postgres'`.","cause":"The library is not installed or the `ENGINE` path in `settings.py` is incorrect.","error":"ModuleNotFoundError: No module named 'django_pg_zero_downtime_migrations'"}]}