Checklist for Moving a Django Database to Managed PostgreSQL
Move a Django app’s self-hosted PostgreSQL to RDS, Cloud SQL, Azure, Neon, or another managed service: check versions and extensions, choose dump/restore or logical replication, handle roles and ownership, plan cutover and rollback, and verify Django afterwards.
Problem statement
Your Django app's PostgreSQL runs on the same VPS or a self-managed server, and you want backups, failover, and patching handled by a managed service. The move itself is a database migration with downtime risk: managed services restrict superuser access and extensions, version differences can break dumps, roles and ownership change, and switching connections must be coordinated with the app. A checklist keeps the cutover short and reversible.
- Inventory database size, PostgreSQL version, extensions, roles, and connection counts.
- Create the managed instance with the same or newer major version, enable required extensions, and size it.
- Choose the copy method:
pg_dump/pg_restorefor small databases, logical replication for minimal downtime. - Rehearse on a copy, measure duration, and plan the cutover: maintenance mode, final sync, switch
DATABASE_URL, verify. - Keep the old database read-only and available for rollback until confidence is high.
Checklist
Assess
- Current PostgreSQL major version; managed service supports it or a newer one.
- Extensions in use (
SELECT * FROM pg_extension) available on the managed service (PostGIS, pg_trgm, unaccent, pgvector, etc.). - Database size and largest tables; growth rate.
- Peak connection count; whether a pooler is needed on the managed side.
- Superuser-dependent features (custom C functions,
ALTER SYSTEM) identified; managed services do not allow them.
Prepare the target
- Instance in the same region as the app, private networking, TLS required.
- Parameters set:
max_connections,work_mem,statement_timeoutfor app roles,log_min_duration_statement. - App role created with least privilege; owner role for migrations.
- Backups with PITR enabled; high availability (multi-AZ) if required.
Copy and rehearse
- Restore a full copy into the managed instance and time it (
pg_restore --jobs N). - Run
python manage.py migrate --planand the test suite against it; check for missing extensions or role errors. - For logical replication: tables have primary keys, publication on the source, subscription on the target, sequences synced at cutover.
- Ownership fixed (
--no-owneron restore, thenALTER ... OWNER TO), and privileges granted to the app role.
Cutover
- Announce the window; enable maintenance mode or stop writes (web, Celery, cron).
- Final sync (restore of a fresh dump, or replication caught up and sequences updated).
- Switch
DATABASE_URLin secrets for all processes; deploy or restart. - Smoke tests: login, create and edit records, run a Celery task, check the admin.
After cutover
- Old database set read-only (
ALTER DATABASE ... SET default_transaction_read_only = on) and kept for rollback. - Monitoring: connections, CPU, slow queries, replication slot cleanup on the old server.
- Backups verified on the managed side with a test restore.
- Old server decommissioned after the agreed period.
Explanation
Managed PostgreSQL services run the same database engine with restricted administrative access and their own operational features. Moving to one is mostly a data copy plus a coordinated connection switch. The risks are compatibility (versions, extensions, superuser-only features), ownership and privileges after restore, and downtime during the final sync. Rehearsing the full process on a copy turns an uncertain migration into a timed runbook, and keeping the old database read-only provides a rollback path.
Edge cases / notes
- Large objects and custom types. Check that
pg_dumphandles them and the target supports them. - Collations. Moving between operating systems can change collation behavior; reindex text indexes after physical copies.
- Connection limits. Managed instances may have lower
max_connections; add PgBouncer or the provider's pooler.
Next steps
- Configure the target with Configure Managed Postgres for Django.
- Minimize downtime with Migrate PostgreSQL With Logical Replication.
- Fix extension errors with Fix Permission Denied to Create Extension.
FAQ
How long will the migration take?
Rehearse to find out. Dump and restore time grows with size; logical replication keeps downtime short regardless.
Will my extensions work on managed PostgreSQL?
Most common ones do, but check the provider's supported list and versions before migrating.
Can I go back if something breaks?
Yes, if you keep the old database intact and read-only and can switch DATABASE_URL back.
Do I need to change Django code?
Usually only settings: the connection URL, TLS options, and possibly a pooler endpoint.