The #1 mistake I see when someone installs pgvector: they compile it from source on their fast build server, ship the binary to a smaller production box, and get hit with Illegal instruction the first time a query runs. The extension loaded fine. CREATE EXTENSION vector; succeeded. Then a real query hit AVX instructions the target CPU didn’t have, and Postgres died.
That failure isn’t a bug – it’s the default. By default, pgvector compiles with -march=native on some platforms for best performance, which can lead to Illegal instruction errors on different machines (per the official pgvector README). Everything downstream in this guide is written to avoid that trap and a handful of others most tutorials skip.
We’ll deploy the Postgres vector extension (currently pgvector v0.8.5) on Linux via APT and from source, with a Docker option, and cover the specific errors you’ll hit in the wrong order.
What pgvector actually is (in one paragraph)
pgvector is a Postgres extension that adds a vector column type plus L2, cosine, and inner-product distance operators, with two approximate-nearest-neighbor index types: IVFFlat and HNSW. The HNSW implementation is based on the Malkov & Yashunin HNSW paper cited in the project’s README. You install it once per Postgres server, then run CREATE EXTENSION vector; inside each database that needs it.
System requirements and the version that matters
Before choosing a version: pgvector 0.8.2 (released February 2026) fixes a buffer overflow in parallel HNSW index builds – CVE-2026-3172 – which can leak sensitive data from other relations or crash the server (PostgreSQL.org announcement). If you’re installing fresh in mid-2026, do NOT pin to anything older than 0.8.2. The current tagged branch in the official README examples is v0.8.5.
| Requirement | Minimum | Recommended for HNSW builds |
|---|---|---|
| PostgreSQL (as of mid-2026) | 13 | 16 or 17.3+ (see errors section) |
| RAM | enough for your dataset + working set | index size fits in maintenance_work_mem |
| Build tools | make, gcc, postgresql-server-dev-N | same |
| Disk | your data + ~1.5x for HNSW graph | SSD |
Debian and Ubuntu packages are available from the PostgreSQL APT Repository; RPM packages from the PostgreSQL Yum Repository (sudo yum install pgvector_18 – replace 18 with your Postgres major version). Package installs skip the -march=native problem entirely because the packages are built for portability. That’s usually the right choice.
Install path 1: APT (recommended for most servers)
If you’re on Debian or Ubuntu with a supported Postgres version, this is a two-minute job:
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
sudo apt-get install postgresql-16-pgvector
Swap 16 for your Postgres major version. Then hop into your database and enable it:
sudo -u postgres psql -d your_db
CREATE EXTENSION vector;
Done. Move to the verification section.
Install path 2: from source (when APT isn’t available)
Source is fine on Linux, but read the whole section first – there’s a step-in-the-wrong-order landmine at the end.
- Install the Postgres dev headers. On Debian/Ubuntu:
sudo apt install postgresql-server-dev-16 build-essential git - Clone the tagged release (from the official README):
git clone --branch v0.8.5 https://github.com/pgvector/pgvector.git && cd pgvector && make && make install(may need sudo). - Enter psql and run
CREATE EXTENSION vector;in each target database.
Here’s the landmine. Deploying the compiled binary to a different machine than the one you built on? Override the CPU-specific optimizations before running make:
make OPTFLAGS=""
sudo make install
This strips the SIMD-specific flags that cause Illegal instruction on mismatched hardware. Building on the same box you’re deploying on? Leave OPTFLAGS alone and get the SIMD speedup.
Install path 3: Docker (with the shm-size gotcha)
The official Docker image is the fastest path to a disposable dev environment:
docker run -d --name pgvector-dev
-e POSTGRES_PASSWORD=changeme
-p 5432:5432
--shm-size=1g
pgvector/pgvector:pg18-trixie
Available image tags (per DeepWiki’s pgvector install reference) follow the pattern pg{13-19}-bookworm (Debian stable) or pg{13-19}-trixie (testing), plus version-pinned tags like 0.8.1-pg18-bookworm. Pin to a specific version for production – pg18-trixie is a floating tag.
Pro tip: If you increase
maintenance_work_mem, make sure--shm-sizeis at least that size to avoid an error with parallel HNSW index builds (official README). Docker’s default shared memory ceiling is low. That’s fine until you setmaintenance_work_mem = '4GB'for a real index build, at which point parallel workers try to allocate shared memory that doesn’t exist and the build crashes with a cryptic message. Match them.
First-time configuration
maintenance_work_mem is the one knob that actually matters on day one – it caps your index builds. Set it at the session level right before creating an index:
SET maintenance_work_mem = '4GB';
SET max_parallel_maintenance_workers = 4;
CREATE INDEX CONCURRENTLY ON items USING hnsw (embedding vector_cosine_ops);
HNSW is the right default for most workloads – reach for IVFFlat only when build time or memory is the binding constraint (ParadeDB tuning guide). And IVFFlat has a nasty timing requirement: build it after representative data is loaded. On an empty or small table, the clustering step produces poor partitions that hurt recall even after more data arrives – you can’t fix it without dropping and rebuilding the index. HNSW doesn’t have this problem; create it any time.
That’s why the standard “install → CREATE EXTENSION → CREATE INDEX → load data” order in most tutorials is quietly wrong for IVFFlat. Load first, index second.
Verify it works
Three checks. Run each one:
-- 1. Extension is registered
SELECT extname, extversion FROM pg_extension WHERE extname = 'vector';
-- 2. The type actually works
SELECT '[1,2,3]'::vector;
-- 3. Distance operator responds
SELECT '[1,2,3]'::vector <-> '[4,5,6]'::vector;
Step 1 returns zero rows? You installed the files but never ran CREATE EXTENSION vector; in this database – every database needs it separately. Step 2 errors with “type vector does not exist”? Same fix. Step 3 should return roughly 5.196 (the L2 distance between those two vectors).
Common install errors (from real GitHub issues)
These are the ones I keep seeing in issue trackers and forum posts:
- fatal error: postgres.h: No such file or directory – Postgres development files aren’t installed. Fix:
apt install postgresql-server-dev-16(matching your PG version). - Illegal instruction on query – you compiled with
-march=nativeon one CPU and are running on another. Rebuild withmake OPTFLAGS=""as covered above. - unresolved external symbol float_to_shortest_decimal_bufn – Postgres 17.0-17.2 has a linker bug that blocks the install cold. The fix: upgrade to Postgres 17.3+ (documented in the official README troubleshooting section).
- warning: no such sysroot directory (macOS) – your Postgres installation points to a path that no longer exists, usually after an Xcode/CLT update. Reinstall Postgres to fix.
- E: Unable to locate package postgresql-14-pgvector – you didn’t add the PGDG APT repo first. Run the
apt.postgresql.org.shscript from step 1 of the APT install path.
Upgrade and uninstall
Upgrading pgvector tripped me up more than the initial install. The extension files and the in-database extension version are two separate things. Install the new files first (same method you used originally – APT, source, Docker), then in each database:
ALTER EXTENSION vector UPDATE;
Miss that step and SELECT extversion FROM pg_extension will keep reporting the old version while the loaded shared library is new – usually harmless, occasionally catastrophic. If you’re upgrading from anything below 0.8.2, this is also your CVE patch. Do it soon.
Uninstall is symmetric:
-- inside each database first
DROP EXTENSION vector CASCADE; -- CASCADE removes dependent columns/indexes
-- then remove the files
sudo apt remove postgresql-16-pgvector # APT install
# or, for source install:
cd pgvector && sudo make uninstall
Warning:CASCADE deletes every column of type vector and every index using it. There is no undo. Take a dump first.
Where to go from here
Your next concrete action, once you’ve verified the install: run EXPLAIN (ANALYZE, BUFFERS) on your first real vector query before you build an index. That baseline sequential-scan number is what you’ll compare against after HNSW – it’s the only honest way to see whether the index is doing what you think it is. For deeper tuning after that baseline, the Crunchy Data pgvector docs cover CREATE INDEX CONCURRENTLY patterns that avoid blocking writes in production.
FAQ
Does pgvector work with managed Postgres like RDS, Supabase, or Cloud SQL?
Yes – most managed providers ship it preinstalled and you just run CREATE EXTENSION vector;. Check the provider’s supported-extensions list and, more importantly, their version – some lag behind mainline and won’t have the 0.8.2 CVE fix yet.
Can I install pgvector without superuser access?
No. CREATE EXTENSION requires superuser or a role explicitly granted the privilege on that database. On managed services this is why they gate the extension list – they run the CREATE EXTENSION for you against a curated allowlist. Ask your DBA or use the provider’s console; there is no user-space workaround.
How much RAM do I actually need for HNSW?
The graph must fit in maintenance_work_mem during the build. For large embedding tables this is typically multiple GB – and if the graph spills to disk mid-build, throughput drops sharply. That’s the single biggest performance surprise people hit. After the build, keep the index resident in Postgres shared buffers or the OS page cache; eviction at query time is the second most common complaint.