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

# Migrate PostgreSQL data using Data sources in ClickHouse Cloud

> Learn how to migrate your PostgreSQL database to ClickHouse Managed Postgres using the built-in Data sources import wizard in ClickHouse Cloud.

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

export const PrivatePreviewBadge = () => {
  return <div className="privatePreviewBadge">
            <div className="privatePreviewIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M5.33301 6.66667V4.66667V4.66667C5.33301 3.194 6.52701 2 7.99967 2V2C9.47234 2 10.6663 3.194 10.6663 4.66667V4.66667V6.66667" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path d="M8.00033 9.33337V11.3334" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path fillRule="evenodd" clipRule="evenodd" d="M11.333 14H4.66634C3.92967 14 3.33301 13.4033 3.33301 12.6666V7.99996C3.33301 7.26329 3.92967 6.66663 4.66634 6.66663H11.333C12.0697 6.66663 12.6663 7.26329 12.6663 7.99996V12.6666C12.6663 13.4033 12.0697 14 11.333 14Z" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            {'Private preview in ClickHouse Cloud'}
        </div>;
};

<h1 id="migrate-managed-postgres">
  Migrate to Managed Postgres using ClickHouse Cloud
</h1>

ClickHouse Cloud includes a built-in import wizard that migrates your external PostgreSQL database into a Managed Postgres service. The wizard handles the source connection, schema export and import, replication settings, and table selection in five guided steps.

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

* Access to your source PostgreSQL database with a user that has replication privileges.
* 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.

<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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/servicecard.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=c211adac667ce7d3197dc066cd4d36b4" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/overview.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=003cd10758ab5092897ed3d1162384fa" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/startimport.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=2acf1028038d1c5089b1e51e63c803ac" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/migrationform.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=ba0850c35240170c9c8f1da801b580c6" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/nextexport.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=45d8a4160ba386bab522235a1ff677cb" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/psqlexport.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=2e7f19508a2c9cc20e1bfcfd4fa40230" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/nextimport.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=ba1be338266c139d7f1a7ff0b4ba7d9a" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/psqlimport.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=9a4444f2ab69064a099b8f3b8a1ba0c0" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/advancedsettings.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=f833af8265d132f04a541da37f2843de" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/tablepicker.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=f16e704f49ec22ff5a1d8e9ea3d66788" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/migrationlist.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=8760944aae0b1866bd8889c90ae95542" 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-sync-clickhouse-operator-docs-7e82242/iWpSV4kCU4b95KM3/images/managed-postgres/pgpg/initialload.png?fit=max&auto=format&n=iWpSV4kCU4b95KM3&q=85&s=3aa79774659d05517d6a1a280dc3947c" 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)
