"I want to do vector search (semantic search) in PostgreSQL" — the entrance to that is pgvector. Without adding a dedicated vector DB, just add one vector type to the Postgres you already use, and you can store AI embedding vectors and search them "in order of closest meaning."
This article is a getting-started guide to take that first step — enabling it on each platform and your first vector search — in the shortest path. "The overall RAG design" and "index tuning" are left to other articles; this article guides you up to "getting it running," faithful to the official documentation and crushing the stumble points.
Rules for this article: extension names, SQL syntax, and each platform's enable procedure are based on the pgvector official README and each cloud's official documentation (as of June 2026). A managed service's pgvector version is tied to the engine's minor and lags behind upstream. Before use, always check
pg_available_extensionson your instance. This article's versions and procedures may change.
1. Understand pgvector in 30 seconds
pgvector is a PostgreSQL extension. Enable it and the following three become usable.
- The
vectortype: a column type to store embedding vectors (numeric arrays like[0.1, -0.3, ...]). - Distance operators: operators that measure "closeness" between vectors, such as
<=>(cosine distance). - ANN indexes: HNSW / IVFFlat. Approximate-nearest-neighbor indexes that return neighbors fast even at millions of rows.
The most important caution first. The extension's name is vector (not pgvector). pgvector is the project name. When enabling in SQL, always write it like this on any platform.
CREATE EXTENSION vector; -- ✅ 正しい(拡張名は vector)
-- CREATE EXTENSION pgvector; -- ❌ これは存在しないのでエラーになる
2. Per platform: enable pgvector
"Run CREATE EXTENSION vector;" is common, but its prerequisites (installing the binary / allow-list / permissions) differ by platform. Reading only your environment's section is enough.
Docker / self-host
The shortest is the official Docker image. The image name is pgvector/pgvector, and the tag is the form "Postgres major + Debian base" (e.g., pg17).
# 公式イメージ(pgvector がプリインストール済みの Postgres)
docker pull pgvector/pgvector:pg17
docker run -d --name pg -e POSTGRES_PASSWORD="$PGPASSWORD" -p 5432:5432 pgvector/pgvector:pg17
To install into existing Postgres from source (pg_config must be on PATH):
cd /tmp
git clone --branch v0.8.3 https://github.com/pgvector/pgvector.git
cd pgvector && make && sudo make install
It also installs via OS packages (NN is the Postgres major).
sudo apt install postgresql-17-pgvector # Debian/Ubuntu(PGDG apt)
sudo dnf install pgvector_17 # RHEL/Fedora(PGDG yum)
brew install pgvector # macOS
Finally, run once on the target database.
CREATE EXTENSION vector;
Supabase
In the dashboard's Database → Extensions, search "vector" and turn the toggle ON, or run it in the SQL editor. By Supabase convention, put it in the extensions schema.
create extension vector with schema extensions;
AWS RDS for PostgreSQL / Aurora PostgreSQL
pgvector is provided as a trusted extension of RDS / Aurora. The binary is already installed, so you just enable it.
-- まず使えるバージョンを確認(マネージドは本家より遅れることがある)
SELECT * FROM pg_available_extensions WHERE name = 'vector';
CREATE EXTENSION IF NOT EXISTS vector;
- Permissions: on PostgreSQL 13 or later, if you have
CREATEprivilege on the database you can install a trusted extension even without beingrds_superuser(12 or earlier needsrds_superuser). - To use an HNSW index, pgvector 0.5.0 or later is required. Older minors may not support it, so check with the
pg_available_extensionsabove.
Neon
Available on all plans. No extra configuration or special privilege needed.
CREATE EXTENSION IF NOT EXISTS vector;
-- 一つ前のバージョンに固定したいとき
-- CREATE EXTENSION vector VERSION '0.7.4';
Google Cloud SQL for PostgreSQL / AlloyDB
Cloud SQL enables without flags. It can be created by members of the cloudsqlsuperuser role (the default postgres user holds it).
CREATE EXTENSION vector; -- 名前は vector(pgvector ではない)
AlloyDB can use, in addition to pgvector, Google's own ScaNN index. With CASCADE, vector is installed at the same time.
CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE; -- alloydb_scann + vector
Azure Database for PostgreSQL — Flexible Server
Only Azure is two-step. First register in the allow-list (azure.extensions), then CREATE EXTENSION. In the allow-list too, the name is vector.
# 1) サーバーパラメータ azure.extensions に VECTOR を許可(CLIの例)
az postgres flexible-server parameter set \
--resource-group <rg> --server-name <server> \
--name azure.extensions --value vector
-- 2) 対象DBで有効化(権限: azure_pg_admin)
CREATE EXTENSION vector;
Quick reference: enable command and required privilege
| Platform | Prep | Enable | Required privilege |
|---|---|---|---|
| Docker / self-host | image or make install | CREATE EXTENSION vector; | superuser |
| Supabase | (preinstalled) | create extension vector with schema extensions; | (UI/SQL) |
| RDS / Aurora | (preinstalled, trusted) | CREATE EXTENSION vector; | CREATE on DB (PG13+) / rds_superuser (PG≤12) |
| Neon | none | CREATE EXTENSION vector; | no special privilege |
| Cloud SQL / AlloyDB | (preinstalled) | CREATE EXTENSION vector; | cloudsqlsuperuser |
| Azure Flexible Server | register in azure.extensions | CREATE EXTENSION vector; | azure_pg_admin |
3. Verification and common errors
Whether it enabled can be checked with the same SQL in any environment.
SELECT extversion FROM pg_extension WHERE extname = 'vector'; -- 入っていればバージョンが返る
SELECT * FROM pg_available_extensions WHERE name = 'vector'; -- 入れられるバージョン
In psql, list with \dx and details with \dx vector. Most stumbles are the following two. Know the meaning and you can isolate them instantly.
| Error | What it really means | Remedy |
|---|---|---|
could not open extension control file ".../vector.control": No such file or directory | the binary isn't installed (not a permission problem) | use make install / the package for the correct PG major / a Docker image with pgvector |
permission denied to create extension "vector" | insufficient role privilege | grant superuser / rds_superuser / cloudsqlsuperuser / azure_pg_admin, or CREATE privilege on the DB |
extension "vector" is not allow-listed for "azure.extensions" setting | on Azure, not registered in the allow-list | add vector to azure.extensions and rerun |
4. Your first vector search
Once enabled, finally the search. Go through the minimal flow of create table → insert → kNN search.
-- 1) 埋め込みを格納するテーブル。vector(3) の 3 は次元数(実際は埋め込みモデルの出力次元に合わせる)
CREATE TABLE items (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content text NOT NULL,
embedding vector(3) NOT NULL
);
-- 2) 投入。ベクトルは '[...]' のテキストリテラルで渡す
INSERT INTO items (content, embedding) VALUES
('りんご', '[1, 2, 3]'),
('みかん', '[1, 2, 4]'),
('自動車', '[9, 8, 1]');
-- 3) クエリベクトルに「意味が近い順」で上位2件(kNN検索)
SELECT id, content, embedding <=> '[1, 2, 3]' AS distance
FROM items
ORDER BY embedding <=> '[1, 2, 3]' -- 小さいほど近い
LIMIT 2;
Distance operators: choose by use
How to measure vector "closeness" is chosen by operator. The iron rule is to match the distance the embedding model assumes.
| Operator | Distance | Main use |
|---|---|---|
<=> | cosine distance | the standard for text embeddings (measures meaning by direction) |
<-> | L2 (Euclidean) distance | general unnormalized vectors |
<#> | negative inner product | inner-product-based models |
<+> | L1 (Manhattan) distance | L1 space |
<~> / <%> | Hamming / Jaccard distance | bit type (binary vectors) |
For text embeddings like OpenAI's text-embedding-3-*, choosing <=> (cosine distance) first is foolproof.
What about real data?: the
'[1,2,3]'above is a hand-written vector for explanation. In reality youINSERTa several-hundred-to-thousand-dimensional vector obtained by passing text to an embedding model (OpenAI, Cohere, your own model, etc.). How to pass it from the app side is handled in detail in the type-safe implementation with TypeScript × Drizzle.
5. The four vector types and the dimension cap
pgvector has four types by use. Start with vector and consider others when needed is enough (YAGNI).
| Type | Size per element | Indexable dimensions | Where to use |
|---|---|---|---|
vector(n) | 4 bytes (float32) | up to 2,000 | standard. this first |
halfvec(n) | 2 bytes (float16) | up to 4,000 | half the memory, high dimensions |
bit(n) | 1 bit | up to 64,000 | binary quantization (Hamming distance) |
sparsevec(n) | stores only non-zero | up to 16,000 non-zero | sparse representation (SPLADE, etc.) |
The n of vector(n) must match the embedding model's output dimensions (a mismatch is a dimension-mismatch error on INSERT = the benefit of being protected by the type). OpenAI's text-embedding-3-large is 3072 dimensions, but it can be shortened with the dimensions parameter, and in my projects I operate at 1024 dimensions (the reason is detailed in the RAG-design article).
6. Your first index (HNSW)
For a few rows, a full scan is enough, but at millions of rows the full scan every time breaks down. So add an approximate-nearest-neighbor (ANN) index. When in doubt, HNSW (strong for continuous additions and can be created in advance on an empty table).
-- コサイン距離で検索するなら vector_cosine_ops(距離演算子と演算子クラスを必ず揃える)
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
-- 検索精度↔速度は、インデックス再構築なしでランタイムに調整できる
SET hnsw.ef_search = 100; -- 既定40。大きいほど高再現率・低速
Whether the index is working is confirmed with EXPLAIN (OK if Index Scan using ..._hnsw_... appears).
EXPLAIN ANALYZE
SELECT id FROM items ORDER BY embedding <=> '[1,2,3]' LIMIT 5;
Important: the index works only for the form "
ORDER BY embedding <operator> ... LIMIT k." The meaning of parameters likem/ef_construction/ef_search, how to choose between HNSW and IVFFlat, and how to measure recall are systematized in the dedicated article the complete pgvector tuning guide.
7. Articles to read next (from here to production)
After "it ran," digging in this order by purpose is the shortest.
- Want to build RAG (AI answers from internal docs) → building production RAG with pgvector (where to put embeddings, hybrid search, idempotent ingest)
- Search is slow / accuracy isn't there → the complete pgvector tuning guide (HNSW/IVFFlat, quantization, measuring recall)
- Torn over a dedicated vector DB → pgvector vs. Pinecone/Qdrant/Weaviate/Milvus selection
- Want a type-safe implementation in TypeScript / Next.js → pgvector × Drizzle ORM × Next.js
Conclusion: the shortest checklist
- The extension name is
vector. Enabling is the one lineCREATE EXTENSION vector;in any environment. - The platform difference is only the "prerequisite": for Docker, the official image
pgvector/pgvector:pg17; only Azure needsazure.extensionspermission first. Check the managed version withpg_available_extensions. - Errors are two lines:
control file not found= binary not installed;permission denied= insufficient privilege. - The first search: a
vector(n)column →INSERT '[...]'→ORDER BY embedding <=> $1 LIMIT k. Text uses<=>. - Add HNSW once it scales:
USING hnsw (embedding vector_cosine_ops), tune accuracy withhnsw.ef_search.
The goodness of pgvector is that you can begin vector search inside the PostgreSQL you already use, with the SQL, permissions, and backups you're used to. In the generative-AI voice chatbot, I consolidated business data and embeddings into pgvector on RDS without adding a dedicated vector DB, and carried production RAG through design, implementation, and operation with one person × generative AI (Claude Code).
"I want to start AI semantic search on my own company's data but don't know where and how to host it" — I can accompany you end-to-end from that entrance through production operation. Feel free to reach out.
References (official documentation)
- pgvector (GitHub, README) — installation (Docker / source / OS package),
CREATE EXTENSION vector, types and distance operators, HNSW / IVFFlat - Supabase — pgvector / AWS RDS extensions / Neon — pgvector / Google Cloud SQL extensions / use pgvector on Azure Flexible Server