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.

Move to managed PostgresAssessversion, extensionsCopy datadump or replicationCutoverfreeze, switch URLVerify, keep oldrollback pathRehearse the copy and cutover until the downtime is predictable.
  1. Inventory database size, PostgreSQL version, extensions, roles, and connection counts.
  2. Create the managed instance with the same or newer major version, enable required extensions, and size it.
  3. Choose the copy method: pg_dump/pg_restore for small databases, logical replication for minimal downtime.
  4. Rehearse on a copy, measure duration, and plan the cutover: maintenance mode, final sync, switch DATABASE_URL, verify.
  5. 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_timeout for 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.
method and downtimeDowntimeNotespg_dump / pg_restoreminutes to hourssimple, size-boundLogical replicationseconds to minutesPG10+, PKs neededProvider migration serviceminutesAWS DMS, GCP DMSPhysical replica!rarely possibleno superuser accessDump and restore for small databases, replication for large or busy onesdjango-deployment.com
Rehearsal tells you which bucket your database falls into.

Copy and rehearse

  • Restore a full copy into the managed instance and time it (pg_restore --jobs N).
  • Run python manage.py migrate --plan and 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-owner on restore, then ALTER ... 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_URL in 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.
cutover windowMaintenance onwrites stoppedFinal syncdump or replicationSwitch DATABASE_URLrestart allSmoke testsweb, workersMaintenance offold DB read-onlyRehearsed steps keep the window short and predictabledjango-deployment.com
With logical replication, the window can shrink to a few minutes.

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_dump handles 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

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.

Keep going