For years the vector database decision was framed as an algorithm contest. Which approximate nearest neighbor index, which distance metric, which recall number on a synthetic workload. That framing sells conference talks and GitHub stars, but it is the wrong axis for most teams. pgvector, Qdrant, and Pinecone do not really compete on whether HNSW beats IVFFlat. They compete on where your vectors live, who is on call for the index build, and what happens to your bill when a filtered query runs against a hundred million rows.
The three sit at different points on one spectrum: the database you already run, a service you run yourself, and a service someone else runs for you. Pick the point first. The index tuning follows.
Three products, three deployment models
pgvector is a Postgres extension. You install it into a database you already operate, and your embeddings sit beside your rows, indexed through the same query planner that handles every other table. The current release is 0.8.7, supporting Postgres 13 and up, with HNSW and IVFFlat indexes that cover vectors up to 2,000 dimensions single-precision, 4,000 half-precision, and 64,000 binary, plus exact nearest neighbor as the no-index default. Sparse vectors, L1 and Hamming distance, and binary quantization with re-ranking are all in the same extension. HNSW landed in the 0.5.0 release back in 2023, recent enough that a lot of “just use pgvector” advice still predates the index that made the extension usable in production.
Qdrant is a standalone server you run as a container, written in Rust and licensed Apache 2.0 (repository). It is purpose-built: dense, sparse, and multi-vector search in one process, with metadata filtering applied during the HNSW traversal rather than bolted on as a pre- or post-filter pass, on a custom storage engine the vendor calls Gridstore (Qdrant). You can run it in Docker in a minute, hand the operations to Qdrant Cloud, or, in its newest form, skip the server entirely: Qdrant Edge is an in-process variant for edge devices that stores and queries locally and synchronizes with a Qdrant server (repository).
Pinecone is the one you never run. It is serverless and fully managed, billed on usage, with no self-hosted edition at all. You get an API endpoint and four pricing dimensions: storage, reads, writes, and egress (Pinecone pricing). Idle indexes cost nothing, which is the whole pitch (Pinecone docs).
That is the real decision tree, and almost everything else is detail.
pgvector: the one you already have
The case for pgvector is consolidation. If you already run Postgres, your vectors are a column, your filters are WHERE clauses, and the join between an embedding and its owning row is free. You inherit ACID, point-in-time recovery, and the replication setup your database team already understands. For a workload in the single-digit millions of vectors with modest filtering, pgvector is often the correct and boring answer. Boring is a compliment here.
The boring answer got less boring in 0.8.0, which fixed the defect that used to sink pgvector in production: overfiltering. In older versions, combining a vector search with a SQL filter applied the filter after the index scan. Ask for 10 results when only 10 percent of your data matched the filter, and with HNSW at the default hnsw.ef_search of 40 you got back roughly four, because the scan stopped after its 40 closest candidates and the filter kept only the ones that matched (README). pgvector 0.8.0 added iterative index scans in two modes, strict_order and relaxed_order, that keep scanning until enough filtered results appear. In a vendor benchmark on 10 million 384-dimensional product embeddings (AWS Database Blog), a complex filtered query that returned 1 percent of requested rows on 0.7.4 returned 100 percent on 0.8.0, and the best configuration ran the basic top-10 query 9.4 times faster than the 0.7.4 baseline (AWS Database Blog).
That is a vendor benchmark, measured by AWS on Aurora, so read it as an upper bound, not a promise. The mechanism is real and reproducible, and it removes the single most common reason teams abandoned pgvector. The relevant knob on any filtered vector query is hnsw.iterative_scan = 'relaxed_order', with hnsw.ef_search tuned to the selectivity of the filter. Two caps bound how far the scan chases recall: hnsw.max_scan_tuples, 20,000 tuples by default, and hnsw.scan_mem_multiplier, which limits scan memory as a multiple of work_mem (pgvector README).
What pgvector does not give you is an easy path past main memory. Building an HNSW index on a few million high-dimensional vectors can consume 10 GB of RAM or more, on your production database, while it runs, for hours at a stretch (Alex Jacobs). Postgres has no good way to throttle an index build, so you end up over-provisioning memory or building indexes on a replica and promoting it over. The README’s mitigations are real but blunt: raise maintenance_work_mem until the graph fits, raise max_parallel_maintenance_workers, and heed the notice telling you when it no longer does (README). The query planner was designed for B-tree selectivity, not vector geometry, and the pre-filter-versus-post-filter call becomes a tuning problem you inherit rather than a behavior you configure. Hybrid search, blending vector similarity with full-text ranking, is something you assemble yourself with reciprocal rank fusion in application code (Jacobs).
The escape hatch is pgvectorscale, a second extension that adds a disk-based index called StreamingDiskANN (Jacobs). It trades RAM for disk I/O and pushes further on a single node, at the cost of another dependency and a build that was still serial and single-threaded in the one published head-to-head: 11.1 hours for 50 million vectors, against Qdrant’s 3.3 (TigerData). It also is not available on managed Postgres such as RDS, so adopting it can mean leaving the managed database that made pgvector attractive in the first place.
Qdrant: the dedicated one you run yourself
Qdrant exists for teams that outgrew pgvector’s operational profile and do not want to pay Pinecone’s prices. Its filtering is the headline feature: conditions are evaluated during graph traversal in a single stage, so a complex filter does not collapse recall the way a naive post-filter does (Qdrant). Hybrid search is native, fusing dense and sparse vectors through reciprocal rank fusion or distribution-based score fusion, with BM25, SPLADE++, and miniCOIL as sparse options and late-interaction models such as ColBERT as multi-vectors (Qdrant). If your retrieval problem is “semantic match plus keyword precision plus a metadata filter,” Qdrant gives you all three in one query without stitching them together by hand.
Memory is the other lever. Qdrant ships scalar, binary, and asymmetric quantization, and the vendor quotes savings in two different units: up to 97 percent on the GitHub README and up to 64-fold on the product page (repository, product page). Treat both as marketing ranges, not a single constant. What matters operationally is that you can trade accuracy for RAM and the trade is a configuration flag. The repository also lists GPU-accelerated indexing for NVIDIA and AMD hardware and io_uring async I/O among current features, which tells you where the engineering effort is going (repository).
Qdrant Cloud is the managed path, and its pricing is resource-based rather than request-based: you pay for the vCPU, memory, and storage your cluster consumes, hourly, with a free tier of one node at half a vCPU, 1 GB of RAM, and 4 GB of disk (pricing). The free tier is a prototype sandbox, not a production tier. Production means a Standard cluster with a 99.5 percent uptime SLA, 99.9 on the Premium tier, and the bill tracks your hardware, not your query count (pricing).
The honest caveat on Qdrant performance is that the most-cited head-to-head benchmark is not neutral. A 2025 study by TigerData, the company behind pgvectorscale, ran 50 million 768-dimensional Cohere embeddings through a fork of ANN-benchmarks, on pgvector 0.6.1 and Qdrant 1.13.4, and found that Postgres with pgvector and pgvectorscale delivered 11.4 times the single-node throughput of Qdrant at 99 percent recall, while Qdrant posted better tail latency. The authors also admitted they struggled to tune Qdrant’s HNSW parameters and that the defaults were not great, which is exactly the kind of confound that makes single-number benchmarks misleading. Read it as: on one tuned node, Postgres can keep up, and Qdrant’s real advantage is horizontal scaling, faster index builds, and operational focus, not raw queries per second on someone else’s misconfigured cluster.
Pinecone: the one you do not run
Pinecone is the choice when the engineering cost of running a vector database exceeds the dollar cost of paying someone else to run it. It is serverless, so there is no cluster to size, no index to rebuild at 3 a.m., and no HNSW parameters to tune. You create an index, upsert vectors, and query. Dense, sparse, and full-text index types are available on every plan, and namespaces give you multitenancy without standing up separate clusters (pricing).
The pricing is the part to read carefully, because it is where Pinecone earns its margin. On the Standard plan, storage is $0.33 per GB per month, write units run $4 to $4.50 per million, and read units run $16 to $18 per million, varying by cloud and region (pricing). Standard carries a $50 monthly minimum and Enterprise $500. Between the free Starter plan (2 GB of storage, 2 million write units, 1 million read units per month) and Standard sits Builder, a flat $20 a month with 10 GB of storage, 5 million write units, and 2 million read units; past those limits, Builder blocks usage rather than billing for it (pricing, docs).
The read-unit definition is the detail that catches teams. A query costs one read unit for every 1 GB of namespace size, with a floor of 0.25 read units per query. A query against a 1 GB namespace costs one unit. The same query against a 100 GB namespace costs 100 units (docs). Your query did not get harder, and it did not return more data, but its bill grew a hundredfold because the namespace grew. At $16 to $18 per million read units (pricing), a high-volume query workload against a large namespace is where Pinecone gets expensive, and the only lever is sharding into smaller namespaces, which then becomes your problem to design.
One wrinkle in the metering works in your favor: a hybrid search that narrows candidates with a text-match filter and ranks the remainder with a dense vector is billed as a single read request, while running the keyword and vector searches separately and merging them client-side is billed as two (docs).
For a workload that fits the model, that is a reasonable trade. For a high-throughput semantic search over a large, frequently filtered corpus, the read-unit math can dwarf what a self-hosted Qdrant cluster would cost on equivalent hardware. The decision is whether zero operations is worth usage-based pricing that grows with index size rather than with query count.
Filtered search is where they actually diverge
Strip away the benchmark numbers and the three products separate most clearly on filtered vector search, which is the query shape real applications use. Nobody runs an unfiltered top-10 over a million vectors in production. They run nearest documents in this tenant, in this date range, with this access level.
pgvector gives you the SQL WHERE clause and, since 0.8.0, an iterative scan that rescues recall when the filter is selective. Its own documentation lays out a ladder: index the filter column when few rows match and exact search wins, move to HNSW with iterative scans when it does not, use a partial index when you filter on a handful of distinct values, and partition the table, per tenant if necessary, when you filter across many (README). It works, and it is now honest about result completeness, but the tuning is yours: ef_search, max_scan_tuples, the choice between strict and relaxed ordering, and the standing question of whether the planner picked the right strategy. Qdrant folds the filter into the traversal and hands you a single API call (Qdrant). Pinecone applies the filter automatically and charges you for it in read units scaled by namespace size (docs).
That is the actual comparison. pgvector is the most expressive and the most work. Qdrant is the most purpose-built. Pinecone is the most opaque and the most expensive per query against a large index.
How to choose
Start from what you already operate, not from a leaderboard.
If you run Postgres and your workload sits in the single-digit millions of vectors with moderate filtering, use pgvector, set hnsw.iterative_scan = 'relaxed_order', and stop. Add pgvectorscale if you need to spill to disk or push past main memory on one box. The case for leaving Postgres has to clear the bar of “I am willing to operate and pay for a second database,” and for most teams it does not clear it. This is also where the cost-aware retrieval-routing and retrieval-quality decisions live, and pgvector’s SQL surface makes both easier to reason about.
If you have outgrown Postgres’s operational profile, or you want native hybrid search and horizontal sharding without building them yourself, run Qdrant. It is the dedicated vector database that behaves like one, with filtering and fusion designed for the query shapes pgvector handles awkwardly. It is also the stronger fit if your retrieval problem looks more like graph versus vector indexing than flat similarity, because its sparse and multi-vector modes cover ground pgvector reaches only through hand-rolled fusion.
If you have no database team, or your vector workload is spiky and you would rather pay per use than staff a cluster, use Pinecone. Accept the read-unit pricing model and shard your namespaces deliberately. The value is the absence of operations, and for a small team that value is genuine. Treat the reranking stage as a separate decision. All three integrate with external rerankers, and none of them removes the need for one when recall matters.
The unifying thread is that none of these products closes the access-control gap that haunts vector pipelines. Row-level security on embeddings is still something you build, in whichever system you pick.
Asterisks
Every number above is a vendor number or a single third-party benchmark, and all of them bend toward the interest of whoever ran them. The AWS figure favors the database AWS sells. The TigerData figure favors the extension TigerData maintains, measured on pgvector 0.6.1 and Qdrant 1.13.4, versions old enough that the pgvector side predates iterative scans entirely (TigerData). The reproducible benchmarks cited here cover pgvector and Qdrant, not Pinecone; for Pinecone you are reading vendor marketing plus community anecdotes, and the anecdotes disagree with each other.
The pgvector RAM figure is real but data-dependent. Ten gigabytes is an order-of-magnitude anchor from one practitioner, not a constant, and your number depends on dimensionality, m, and ef_construction (Jacobs). Qdrant’s quantization savings are quoted in two different units by the same vendor, which should calibrate how precisely you trust either figure. And the deployment-model framing has an edge case of its own: Pinecone’s bring-your-own-cloud option runs in your account with outbound-only, zero-access operations (pricing), which narrows the “you do not run it” characterization without changing the pricing model.
None of this changes the decision tree. It changes how much faith you put in any single benchmark before you provision hardware and run your own.
Join the discussion
Share a useful perspective or ask a question about this article.