> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-fix-nav-issues.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate PostgreSQL data using Data sources in ClickPipes

> Learn how to migrate your PostgreSQL database to ClickHouse Managed Postgres using ClickPipes.

export const Image = ({img, alt, size}) => {
  return <Frame>
      <img src={img} alt={alt} />
    </Frame>;
};

export const galaxyOnClick = eventName => () => {
  try {
    if (typeof window !== "undefined" && window.galaxy && eventName) {
      window.galaxy.track(eventName, {
        interaction: "click"
      });
    }
  } catch (e) {}
};

export const BetaBadge = ({link, galaxyTrack, galaxyEvent}) => {
  if (link) {
    return <a href={link} target="_blank" rel="noopener noreferrer" className="betaBadge" onClick={galaxyTrack && galaxyEvent ? galaxyOnClick(galaxyEvent) : undefined}>
                <Icon />
                <span>Beta</span>
            </a>;
  }
  return <div className="betaBadge">
            <Icon />
            <span>
                Beta feature. 
                <u>
                    <a href="/docs/beta-and-experimental-features#beta-features">
                        Learn more.
                    </a>
                </u>
            </span>
        </div>;
};

ClickHouse Cloud now offers ClickPipes to migrate your external PostgreSQL database into a Managed Postgres service. This built-in integration provides a streamlined experience to connect to your source database, export the schema, import it into Managed Postgres, and set up continuous replication.

<h2 id="prerequisites">
  Prerequisites
</h2>

* Access to your source PostgreSQL database with a user that has replication privileges. Follow the setup guide for your source:
  * [Amazon RDS Postgres](/integrations/clickpipes/postgres/source/rds)
  * [Amazon Aurora Postgres](/integrations/clickpipes/postgres/source/aurora)
  * [Supabase Postgres](/integrations/clickpipes/postgres/source/supabase)
  * [Google Cloud SQL Postgres](/integrations/clickpipes/postgres/source/google-cloudsql)
  * [Azure Flexible Server for Postgres](/integrations/clickpipes/postgres/source/azure-flexible-server-postgres)
  * [Neon Postgres](/integrations/clickpipes/postgres/source/neon-postgres)
  * [Crunchy Bridge Postgres](/integrations/clickpipes/postgres/source/crunchy-postgres)
  * [TimescaleDB](/integrations/clickpipes/postgres/source/timescale)
  * [Generic Postgres Source](/integrations/clickpipes/postgres/source/generic) for any other provider or self-hosted instance
* A ClickHouse Managed Postgres service as the migration target. If you don't have one yet, see the [quickstart](/products/managed-postgres/quickstart).
* `pg_dump` and `psql` installed on your local machine. Both ship with the standard PostgreSQL client tools.

<h2 id="considerations">
  Considerations before migrating
</h2>

* **DDL propagation**: continuous replication (CDC) captures DML operations and `ADD COLUMN`. Other DDL changes such as `DROP COLUMN` and `ALTER COLUMN` aren't propagated and must be applied manually on the target.

<Note>
  If you run into issues during migration, check the [Managed Postgres Migrations FAQ](/products/managed-postgres/migrations/faq) for common errors and solutions.
</Note>

<h2 id="step-1-connect">
  Step 1: Connect to your source database
</h2>

Open the [ClickHouse Cloud console](https://clickhouse.cloud) and select your Managed Postgres service.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/servicecard.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=9489a06a98d75e91348b5da294525e76" alt="Managed Postgres service card in the ClickHouse Cloud services list" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/servicecard.png" />

In the left sidebar, click **Data sources**.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/overview.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=9e1cb6b5a839a052819244c60a9ee17d" alt="Data sources entry in the Managed Postgres service sidebar" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/overview.png" />

Click **Start import**.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/startimport.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=b92bd9233ee8a30016d92213e4d78dd9" alt="Data sources page with Start import button" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/startimport.png" />

Fill in the connection details for your source PostgreSQL database: host, port, username, password, and database name. Enable **TLS** if your source requires it.

If you require a private connection to your source database, you can opt for **SSH tunneling** and provide the necessary SSH details. This allows the migration to securely connect to databases that aren't publicly accessible.

Choose an ingestion method:

* **Initial load + CDC** — copies existing data, then keeps the target in sync with ongoing changes.
* **Initial load only** — one-time copy, no ongoing replication.
* **CDC only** — skips the initial copy and replicates only new changes from this point forward.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/migrationform.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=ff63743500aeb934937d695cf5354173" alt="Step 1: source database connection form with ingestion method options" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/migrationform.png" />

Click **Next**.

<h2 id="step-2-export-schema">
  Step 2: Export your database schema
</h2>

The wizard displays a `pg_dump` command pre-filled with your source connection details. Run it in a terminal:

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/nextexport.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=0d1901993cfa84d0a6befde992db8f14" alt="Step 2: pg_dump command for schema export" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/nextexport.png" />

```shell theme={null}
pg_dump \
  -h <source_host> \
  -U <source_user> \
  -d <source_database> \
  --schema-only \
  -f pg.sql
```

This creates `pg.sql` in your current directory.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/psqlexport.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=cb98d175bc0c906e051554bf795ed0fc" alt="Terminal output after running pg_dump" size="lg" border width="1452" height="422" data-path="images/managed-postgres/pgpg/psqlexport.png" />

Click **Next**.

<h2 id="step-3-import-schema">
  Step 3: Import the schema into your Managed Postgres service
</h2>

Select the destination database from the dropdown, or click **Create a new database** to provision one.

The wizard displays a `psql` command to apply the schema dump to your Managed Postgres service. Run it in a terminal:

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/nextimport.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=49633961f0514ab7c0f1a8f59840faa4" alt="Step 3: psql command for schema import" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/nextimport.png" />

```shell theme={null}
psql \
  -h <target_host> \
  -p 5432 \
  -U <target_user> \
  -d <target_database> \
  -f pg.sql
```

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/psqlimport.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=fb12c7401ae28d74b5e41ef6f5edf672" alt="Terminal output after running psql schema import" size="lg" border width="2362" height="762" data-path="images/managed-postgres/pgpg/psqlimport.png" />

Click **Next**.

<h2 id="step-4-ingestion-settings">
  Step 4: Configure ingestion settings
</h2>

Specify the publication to use for logical replication. If you leave this blank, a publication is created automatically.

Expand **Advanced replication settings** to tune throughput:

| Setting                               | Default | Description                                   |
| ------------------------------------- | ------- | --------------------------------------------- |
| Sync interval (seconds)               | 10      | How frequently the replication slot is polled |
| Parallel threads for initial load     | 4       | Number of threads for the bulk copy phase     |
| Pull batch size                       | 100,000 | Rows fetched per replication batch            |
| Snapshot number of rows per partition | 100000  | Partition size for large table snapshots      |
| Snapshot number of tables in parallel | 1       | Tables snapshotted concurrently               |

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/advancedsettings.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=1a4ef969ee9e5407ba3552aa71cf9b2a" alt="Step 4: ingestion settings form with publication and advanced replication options" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/advancedsettings.png" />

Click **Next**.

<h2 id="step-5-select-tables">
  Step 5: Select tables
</h2>

Select the tables you want to replicate. Tables are grouped by schema. Select individual tables or expand a schema to pick all of them.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/tablepicker.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=f5e0d473a2121f18a235d90f044bb245" alt="Step 5: table picker grouped by schema with Create migration button" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/tablepicker.png" />

Click **Create migration**.

<h2 id="monitor">
  Monitor the migration
</h2>

After creating the migration, you'll see it listed in **Data sources** with a **Running** status.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/migrationlist.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=2a80626d8c0b4790cc19900b6cdc1f67" alt="Data sources list showing a running migration" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/migrationlist.png" />

Click the migration to open the detail view. The **Tables** tab shows the initial load progress for each table, including rows processed, partitions, and average time per partition. The **Metrics** tab shows replication lag and throughput once CDC begins.

<Image img="https://mintcdn.com/private-7c7dfe99-fix-nav-issues/qT0j4CNmQubVqREl/images/managed-postgres/pgpg/initialload.png?fit=max&auto=format&n=qT0j4CNmQubVqREl&q=85&s=147ea41c4664e2fefd47342eb3cfebbd" alt="Migration detail view showing initial load stats per table" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/initialload.png" />

<h2 id="post-migration">
  Post-migration tasks
</h2>

Once the initial load is complete and, if using CDC, replication lag is near zero:

**Validate row counts.** Spot-check critical tables on both source and target before switching traffic:

```sql theme={null}
SELECT COUNT(*) FROM public.orders;
```

**Stop writes on the source.** Pause application writes. To enforce read-only mode during cutover:

```sql theme={null}
ALTER DATABASE <source_db> SET default_transaction_read_only = on;
```

**Confirm replication is caught up.** Compare the latest row on source and target:

```sql theme={null}
-- Run on both source and target
SELECT MAX(id), MAX(updated_at) FROM public.orders;
```

**Reset sequences.** Align sequences with the current maximum values in each table:

```sql theme={null}
DO $$
DECLARE r RECORD;
BEGIN
    FOR r IN
        SELECT
            n.nspname AS schema_name,
            c.relname AS table_name,
            a.attname AS column_name,
            pg_get_serial_sequence(format('%I.%I', n.nspname, c.relname), a.attname) AS seq_name
        FROM pg_class c
        JOIN pg_namespace n ON n.oid = c.relnamespace
        JOIN pg_attribute a ON a.attrelid = c.oid
        WHERE c.relkind = 'r'
            AND a.attnum > 0
            AND NOT a.attisdropped
            AND n.nspname NOT IN ('pg_catalog', 'information_schema')
    LOOP
        IF r.seq_name IS NOT NULL THEN
            EXECUTE format(
                'SELECT setval(%L, COALESCE((SELECT MAX(%I) FROM %I.%I), 0) + 1, false)',
                r.seq_name, r.column_name, r.schema_name, r.table_name
            );
        END IF;
    END LOOP;
END $$;
```

**Cut over application traffic.** Point reads and writes to your Managed Postgres service and monitor for errors, constraint violations, and replication health.

**Clean up.**  Once you've cut over and confirmed the new service is healthy, delete the migration from **Data sources**. If you used CDC, drop the replication slot from the source to free resources:

```sql theme={null}
SELECT pg_drop_replication_slot('<slot_name>');
```

<h2 id="next-steps">
  Next steps
</h2>

* [Managed Postgres quickstart](/products/managed-postgres/quickstart)
* [Managed Postgres connection details](/products/managed-postgres/connection)
* [ClickPipes Postgres FAQ](/integrations/clickpipes/postgres/faq)
