PostgreSQL to Convex: How to Move Your Data

Replicate PostgreSQL to Convex with Airbyte. CDC and incremental deletes, Convex table naming rules, deduped snapshots, and why full refreshes hurt.

Summarize with AI:

Replicating PostgreSQL to Convex moves data from a relational database into a reactive backend, so an application built on Convex can read records that are mastered somewhere else. This is a different shape of pipeline from the usual warehouse load: the destination is serving an application rather than answering analytical queries, which raises the stakes on keeping it accurate. Airbyte builds it with no code.

The encouraging part is that Convex is unusually well equipped as a destination. It supports all four sync modes, and unlike many destinations it handles change data capture and replicating incremental deletes.

CapabilitySupportedWhat it means for this pipeline
Sync modesAll fourIncluding incremental append and incremental dedup
CDC and deletesYesRows deleted in Postgres can be removed downstream
Dedup historySnapshot onlyConvex stores a deduped snapshot, not a history table
Table namingRestrictedLetters, digits, underscores, and no leading underscore
Full refresh at scaleDiscouragedPerformance suffers with large, frequent full refreshes

Why table naming will bite you first

Each stream is written to a table in Convex, and Convex's table naming rules apply. Table names may contain only letters, digits, and underscores, and may not start with an underscore.

Postgres is considerably more permissive. Quoted identifiers allow hyphens, dots, and spaces, and plenty of production schemas contain at least one table that would not survive the trip. Check your table names against the rule before the first sync rather than debugging a partial failure afterwards, and rename at the connection level where Airbyte lets you.

Each record becomes a document in Convex and is assigned _id and _creationTime fields during the sync. Note what that means: the creation time reflects when the document was written to Convex, not when the row was created in Postgres. If your application logic depends on record age, use your own timestamp column rather than the assigned one.

Choosing a sync mode

Convex supports full refresh overwrite, full refresh append, incremental append, and incremental dedup. For a destination backing an application, incremental dedup is usually right, because it gives you a table mirroring the current state of your Postgres data.

One difference from other destinations is worth understanding. For incremental dedup, Convex does not store a history table the way destinations relying on dbt do; it stores a deduped snapshot instead. That is generally what an application wants, but if you were counting on a slowly changing dimension style history of every version, it is not there and you would need to model it yourself.

Airbyte's documentation is direct that performance may suffer with large, frequent full refresh syncs, and recommends incremental modes especially for large tables. Take that seriously here more than on a warehouse destination, because the system you are loading into is also serving your application's reads.

Setting up Postgres CDC

Because Convex can replicate incremental deletes, CDC is worth the setup effort on this pipeline in a way it is not everywhere. A cursor-based sync would leave rows in Convex that no longer exist in Postgres, and an application reading stale records is a worse outcome than a stale dashboard.

On the Postgres side, set wal_level to logical, create a replication slot using the pgoutput plugin, create a publication covering your tables, and give the Airbyte user the REPLICATION attribute plus SELECT on those tables. Changing the WAL level requires a restart, so plan a window.

Then set up monitoring before you set up anything else. A replication slot retains write-ahead log segments until they are read, so a paused connection or a repeatedly failing sync accumulates disk on your primary, and a full disk stops Postgres accepting writes. Alert on slot lag and drop slots you no longer consume.

One design question specific to this pairing

Airbyte syncs are batch jobs running on a schedule, while Convex is a reactive backend where clients expect changes to propagate promptly. Those two facts sit in mild tension, and the gap between them is your sync interval.

That is fine for reference data, catalogues, configuration, and anything else where minutes of staleness are acceptable. It is not a substitute for writing directly to Convex from your application when a user expects to see their own change immediately. Decide which category each table falls into before you replicate it, and keep the user-facing writes in the application.

Frequently asked questions

Will deletes propagate to Convex?

Yes, with Postgres CDC. Convex supports change data capture and replicating incremental deletes, which is why CDC is worth configuring on this pipeline.

Why did a table fail to create?

Convex table names may contain only letters, digits, and underscores, and may not begin with an underscore. Postgres allows names that break those rules, so check yours before the first sync.

Does incremental dedup keep a change history?

No. Convex stores a deduped snapshot rather than a history table. If you need every version of a record, model that yourself.

Can I run frequent full refreshes?

You can, but performance may suffer with large, frequent full refresh syncs. Prefer incremental modes, particularly for large tables and particularly when Convex is serving live application reads.

Get your PostgreSQL data into Convex

Check your table names against Convex's rules, configure CDC so deletes propagate, prefer incremental modes over full refreshes, and keep user-facing writes in the application. For loading Convex from a different source, see our guide to n8n to Convex. For the same source into an analytical store, see PostgreSQL to ClickHouse.

Start syncing now →

Integrate with 600+ apps using Airbyte

Move data from 600+ sources into warehouses, lakes, and beyond. Set up pipelines in minutes with pre-built connectors and the Connector Builder.