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

# Migrate to Managed Postgres using 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 = "lg"}) => {
  const normalizedSize = ["sm", "md", "lg"].includes(size) ? size : "lg";
  return <div className={`ch-image-${normalizedSize}`}>
      <Frame>
        <img src={img} alt={alt} />
      </Frame>
    </div>;
};

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 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.

<Steps>
  <Step title="Connect to your source database" id="step-1-connect">
    Open the [ClickHouse Cloud console](https://clickhouse.cloud) and select your Managed Postgres service.

    <Image img="https://mintcdn.com/private-7c7dfe99-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/servicecard.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=f8826636887fe7b118c4c00a0bd9578b" 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.webp" />

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

    <Image img="https://mintcdn.com/private-7c7dfe99-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/overview.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=86e18c7c65d60b7fcdfb32db325e77a7" alt="Data sources entry in the Managed Postgres service sidebar" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/overview.webp" />

    Click **Start import**.

    <Image img="https://mintcdn.com/private-7c7dfe99-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/startimport.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=f6a3da9f1492a9235cee478f3f1b1a1a" alt="Data sources page with Start import button" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/startimport.webp" />

    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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/migrationform.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=1ca548d289f81ce13369823774723749" 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.webp" />

    Click **Next**.
  </Step>

  <Step title="Export your database schema" id="step-2-export-schema">
    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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/nextexport.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=4f27df741bb6fc41d0548342a9c8d0a9" alt="Step 2: pg_dump command for schema export" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/nextexport.webp" />

    ```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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/psqlexport.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=e48305b8d3659b2ba10570af53e10b10" alt="Terminal output after running pg_dump" size="lg" border width="1452" height="422" data-path="images/managed-postgres/pgpg/psqlexport.webp" />

    Click **Next**.
  </Step>

  <Step title="Import the schema into your Managed Postgres service" id="step-3-import-schema">
    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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/nextimport.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=844f72cdea7fcf986b9d8153f69c93e1" alt="Step 3: psql command for schema import" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/nextimport.webp" />

    ```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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/psqlimport.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=555e9dffc3ed6dcba545eef3bbb4591b" alt="Terminal output after running psql schema import" size="lg" border width="2362" height="762" data-path="images/managed-postgres/pgpg/psqlimport.webp" />

    Click **Next**.
  </Step>

  <Step title="Configure ingestion settings" id="step-4-ingestion-settings">
    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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/advancedsettings.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=74de23839a27e8c6c650cddb9a4ee0f7" 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.webp" />

    Click **Next**.
  </Step>

  <Step title="Select tables" id="step-5-select-tables">
    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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/tablepicker.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=02f4378fc1b0be79d5bcf56edc0c7912" 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.webp" />

    Click **Create migration**.
  </Step>
</Steps>

<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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/migrationlist.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=df88c124c6ac4b1e2c1d4c340e91102a" alt="Data sources list showing a running migration" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/migrationlist.webp" />

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-postgresql-tls-support/1LSgqsUxf0mXbdsL/images/managed-postgres/pgpg/initialload.webp?fit=max&auto=format&n=1LSgqsUxf0mXbdsL&q=85&s=dec2876bafa8c8b1d3a01db8ebcc5e05" alt="Migration detail view showing initial load stats per table" size="lg" border width="3680" height="2392" data-path="images/managed-postgres/pgpg/initialload.webp" />

<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)
