Migrate from pgvector to Qdrant

What You Need from Postgres

  • Connection URL — a standard Postgres connection string
  • Table name — the table containing your vector data

Concept Mapping

pgvectorQdrantNotes
TableCollectionOne-to-one mapping
RowPointEach row becomes a point
vector columnVectorMapped automatically
Other columnsPayloadAll non-vector columns become payload fields
vector_cosine_opsCosinepgvector returns distance (1 - similarity); Qdrant returns similarity
vector_l2_opsEuclidDirect mapping
vector_ip_opsDotpgvector uses negative inner product for ordering; scores will be inverted

Run the Migration

docker run --net=host --rm -it registry.cloud.qdrant.io/library/qdrant-migration pg \
    --pg.url 'postgres://user:password@host:5432/dbname' \
    --pg.table 'your_embeddings_table' \
    --qdrant.url 'https://your-instance.cloud.qdrant.io:6334' \
    --qdrant.api-key 'your-qdrant-api-key' \
    --qdrant.collection 'your-collection'

Selecting Specific Columns

By default, all columns are migrated. Use --pg.columns to select specific ones:

docker run --net=host --rm -it registry.cloud.qdrant.io/library/qdrant-migration pg \
    --pg.url 'postgres://user:password@host:5432/dbname' \
    --pg.table 'your_embeddings_table' \
    --pg.columns 'id,embedding,title,category' \
    --qdrant.url 'https://your-instance.cloud.qdrant.io:6334' \
    --qdrant.api-key 'your-qdrant-api-key' \
    --qdrant.collection 'your-collection'

All pgvector-Specific Flags

FlagRequiredDescription
--pg.urlYesPostgres connection string
--pg.tableYesTable name to migrate
--pg.key-columnYesColumn to use as point ID
--pg.columnsNoComma-separated columns to migrate (default: all)
--migration.num-workersNoParallel workers (default: number of CPU cores)

Qdrant-Side Options

FlagDefaultDescription
--qdrant.distance-metriccosineDistance metric per vector field (map format)

Gotchas

  • Partition structure: If you had manual partitions in pgvector (common at scale), verify that all partitions were migrated, not just the primary table.
  • NULL handling: PostgreSQL NULLs may be dropped during export. Check that optional fields are represented correctly in Qdrant payloads.
  • Index type and recall: pgvector supports IVFFlat and HNSW. If your baseline was captured with IVFFlat (lower recall), Qdrant’s HNSW may return better results. This looks like a “mismatch” but is an improvement.
  • Row count approximation: Postgres’s n_live_tup is an estimate, not an exact count. Use SELECT COUNT(*) FROM your_table for accurate comparison during migration verification.

After Migration: Keeping Postgres and Qdrant in Sync

If you continue using Postgres as your source of truth alongside Qdrant, you’ll need a sync strategy. The Data Synchronization Guide covers three approaches from simple dual-writes to production-grade Change Data Capture.

Next Steps

After migration, verify your data arrived correctly with the Migration Verification Guide.

Was this page useful?

Thank you for your feedback! 🙏

We are sorry to hear that. 😔 You can edit this page on GitHub, or create a GitHub issue.