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.

Warning
Third-party tools can change. Confirm compatibility with your PostgreSQL versions and environment before production use. Logical replication has upstream restrictions (for example, large objects through PostgreSQL 15).

Prerequisites

Before you begin, make sure you have:

  • Access to the source cluster (DigitalOcean Managed PostgreSQL), including the default admin user doadmin and its password. Find connection details in the cluster’s Overview tab. Client connections typically use port 25061. 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_dump and pg_restore from a PostgreSQL version between the source and target (see the upstream README). On Linux, install the appropriate postgresql-client package.
  • Python 3.12 or newer for current aiven-db-migrate releases.
  • 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" --validate

Alternatively, you can set variables inline:

SOURCE_SERVICE_URI=... TARGET_SERVICE_URI=... pg_migrate --validate

pg_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 target

You 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: plpython3u
  • aiven_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-lag to wait until lag is within a specified threshold
  • --stop-replication to remove replication objects after migration
  • --force-method to force replication or dump

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

  1. Stop application traffic to the source cluster.
  2. Allow replication to catch up (use --max-replication-lag or monitor manually).
  3. Point applications to the target cluster and resume traffic.
  4. Remove publications, subscriptions, and replication slots as described in the upstream documentation.

Next Steps

We can't find any results for your search.

Try using different keywords or simplifying your search terms.