Skip to content

Quick start local ​

A container with PostgreSQL, postvec and one local model (MiniLM). Inference runs inside that container.

The same SQL against postvec-server is quick start remote.

1. Run the local image ​

docker run -d --name postvec \
  -e POSTGRES_PASSWORD=demo \
  -e POSTGRES_USER=app \
  -e POSTGRES_DB=app \
  -p 127.0.0.1:5433:5432 \
  ghcr.io/univec-ai/postvec:0.5.0-2-pg18-local

Wait until status is healthy (PostgreSQL is up and MiniLM is loaded):

bash
docker inspect --format '{{.State.Health.Status}}' postvec

The image creates the extension in POSTGRES_DB on first initialization only.

Optional

postvec-healthcheck exits 0 when the worker and engine are ready. postvec.embed() returning 384-d also proves inference:

bash
docker exec postvec postvec-healthcheck
docker exec -i postvec psql -U app -d app -c \
  "SELECT vector_dims(postvec.embed(
     'the isolated image performs inference',
     'sentence-transformers-all-minilm-l6-v2'
   )::vector);"

2. Enable a column and insert ​

bash
docker exec -i postvec psql -U app -d app <<'SQL'
CREATE TABLE docs (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  body text,
  category text
);

SELECT postvec.enable(
  'public.docs', 'body',
  model => 'sentence-transformers-all-minilm-l6-v2',
  create_fts_index => true
);

INSERT INTO docs (body, category) VALUES
  ('quarterly revenue guidance was raised after strong subscription growth',
   'finance'),
  ('migrating embedding models normally requires re-embedding source text',
   'engineering'),
  ('the office plants need watering twice a week',
   'office');

SELECT relation, pending_jobs, dead_jobs
  FROM postvec.status();
SQL

Vectors fill asynchronously. After INSERT commits, wait until pending_jobs = 0 and every body_semantic is non-NULL:

bash
docker exec -it postvec psql -U app -d app

then (in psql):

sql
SELECT count(*) FILTER (WHERE body_semantic IS NOT NULL) AS filled,
       count(*) AS total
  FROM docs;

Expected

filled = total, pending_jobs = 0, dead_jobs = 0. On this image that is usually a second or two.

Index (one-time):

sql
SELECT postvec.create_vector_index('public.docs', 'body');

Search:

sql
SELECT d.id, d.body,
       round(s.rrf_score::numeric, 5) AS score,
       s.semantic_rank, s.fts_rank
  FROM postvec.search(
         'public.docs', 'body',
         'switching AI models without redoing the work'
       ) AS s
  JOIN public.docs AS d ON d.id = s.pk_value::bigint
 ORDER BY s.rrf_score DESC;

Expected

The engineering row ranks highly despite almost no keyword overlap. status().has_vector_index is true.

4. Remove the container ​

bash
docker rm -f postvec

An existing cluster uses packages then configure. RDS, Aurora, Cloud SQL, Azure, Supabase and Neon use managed PostgreSQL.