How to Migrate from Managed to Self-Managed PostgreSQL
Validated on 14 May 2026 • Last edited on 14 May 2026
PostgreSQL is an open source, object-relational database built for extensibility, data integrity, and speed. Its concurrency support makes it fully ACID-compliant, and it supports dynamic loading and catalog-driven operations to let users customize its data types, functions, and more.
You can copy data from a DigitalOcean Managed PostgreSQL cluster to a self-managed PostgreSQL deployment (for example, on a Droplet). For migrations into DigitalOcean (the reverse direction), see continuous migration into DigitalOcean.
This process uses the third-party tool aiven-db-migrate (see the repository license). It supports PostgreSQL 12 through 17 when migrating to the same or newer major version. Migrations to older major versions are not supported.
For requirements, logical replication behavior, role and extension handling, and cleanup steps (publications and subscriptions), see the project README.
Prerequisites
Before you begin, make sure you have:
- Access to the source cluster (DigitalOcean Managed PostgreSQL), including the default admin user
doadminand its password. Find connection details in the cluster’s Overview tab. Client connections typically use port25061. See How to Connect to PostgreSQL Database Clusters. - A target PostgreSQL instance you manage, accessible from the migration host, with credentials that can create databases and roles as required by the migration.
pg_dumpandpg_restorefrom a PostgreSQL version between the source and target (see the upstream README). On Linux, install the appropriatepostgresql-clientpackage.- Python 3.12 or newer for current
aiven-db-migratereleases. - Network connectivity between the migration host and the managed cluster. This may require adding the host to trusted sources, or configuring VPC networking. Ensure the cluster accepts connections required for logical replication if that method is used.
Install build dependencies on the migration host as needed (for example, a C compiler and Python development headers if pip builds packages from source). See the repository documentation for details.
Step 1: Install aiven-db-migrate
Clone the repository, create a virtual environment, and install the tool:
git clone https://github.com/aiven/aiven-db-migrate.git
cd aiven-db-migrate
python3 -m venv venv
source venv/bin/activate
make
pip install .This installs the pg_migrate CLI. If pg_dump and pg_restore are not in /usr/, pass --pgbin with the directory that contains those binaries (see the upstream README).
Step 2: Run Validation
Set service URIs (PostgreSQL connection strings). Use sslmode=require for DigitalOcean managed clusters.
export SOURCE_SERVICE_URI="postgres://doadmin:<password>@<managed-host>:25061/<database>?sslmode=require"
export TARGET_SERVICE_URI="postgres://<user>:<password>@<target-host>:5432/<database>?sslmode=require"Run validation:
pg_migrate -s "$SOURCE_SERVICE_URI" -t "$TARGET_SERVICE_URI" --validateAlternatively, you can set variables inline:
SOURCE_SERVICE_URI=... TARGET_SERVICE_URI=... pg_migrate --validatepg_migrate does not print one fixed “validation passed” line. Treat exit code 0 as success. Each line is tab-separated fields (timestamp, migration id, logger name such as PGMigrate, level, then the message). For example:
2025-04-11 13:16:47,123 a1b2 PGMigrate INFO Database 'defaultdb' already exists in targetYou may also see INFO lines whose message portion mentions extensions (for example text containing Extension 'uuid-ossp' is installed in source and target database 'defaultdb').
A WARNING about large objects and logical replication can still appear when validation succeeds. See the upstream README for replication limits, typical validation messages, and subscription or publication cleanup steps.
A non-zero exit code usually ends with an exception line. The important part is the message after the colon, for example:
aiven_db_migrate.migrate.errors.PGMigrateValidationFailedError: Languages not installed in target: plpython3uaiven_db_migrate.migrate.errors.PGMigrateValidationFailedError: Extension 'postgis' is not available for installation in target
Failure messages come from the checks in aiven-db-migrate (versions, extensions, languages, connectivity, flags such as --no-createdb, and so on). Compare with your own terminal output or the aiven-db-migrate sources.
Step 3: Run the Migration
Start the migration with the same source and target URIs:
pg_migrate -s "$SOURCE_SERVICE_URI" -t "$TARGET_SERVICE_URI"By default, logical replication continues after the initial data copy so you can verify the target before cutover. The upstream README describes how to list and drop subscriptions and publications created by the tool (typically prefixed with aiven_db_migrate_).
Common options include:
--max-replication-lagto wait until lag is within a specified threshold--stop-replicationto remove replication objects after migration--force-methodto forcereplicationordump
Managed PostgreSQL does not provide full superuser access. Some role or replication operations may fail or require workarounds compared to a self-managed source. Review tool output and the README for details on role migration (including placeholder passwords) and extension requirements.
Cutover
- Stop application traffic to the source cluster.
- Allow replication to catch up (use
--max-replication-lagor monitor manually). - Point applications to the target cluster and resume traffic.
- Remove publications, subscriptions, and replication slots as described in the upstream documentation.
Next Steps
- To import a dump into DigitalOcean instead, see How to Import PostgreSQL Databases or How to Migrate PostgreSQL Databases to DigitalOcean.
- For connection security on the managed cluster, see How to Secure PostgreSQL Managed Database Clusters.