Self-hosted Upstash Vector-compatible HTTP proxy backed by Redis Stack.
Drop-in replacement for @upstash/vector — point the SDK at your own server instead of Upstash's cloud. Uses Redis Stack (RediSearch) for HNSW vector indexing. Sibling project to up-redis (same idea, but for vectors).
git clone https://github.com/Coriou/up-vector.git
cd up-vector
cp .env.example .env
# Edit .env — set UPVECTOR_TOKEN to a secret of your choice
docker compose up -dThe API is now available at http://localhost:8080.
Just swap the URL and token — everything else stays the same:
import { Index } from "@upstash/vector"
const index = new Index({
url: "http://localhost:8080", // ← your up-vector instance
token: "your-token-here",
})
// Upsert vectors
await index.upsert([
{ id: "doc-1", vector: [0.1, 0.2, 0.3], metadata: { title: "Hello" } },
{ id: "doc-2", vector: [0.4, 0.5, 0.6], metadata: { title: "World" } },
])
// Query with KNN similarity search
const results = await index.query({
vector: [0.1, 0.2, 0.3],
topK: 5,
includeMetadata: true,
filter: "title = 'Hello'",
})
// All SDK methods work: fetch, delete, update, range, reset, info, namespacesWorks with any language — just send HTTP requests:
# Upsert vectors
curl -X POST http://localhost:8080/upsert \
-H "Authorization: Bearer your-token-here" \
-H "Content-Type: application/json" \
-d '[{"id":"doc-1","vector":[0.1,0.2,0.3],"metadata":{"title":"Hello"}}]'
# Query
curl -X POST http://localhost:8080/query \
-H "Authorization: Bearer your-token-here" \
-H "Content-Type: application/json" \
-d '{"vector":[0.1,0.2,0.3],"topK":5,"includeMetadata":true}'Implements the Upstash Vector REST API, validated by 198 tests including 65 using the real @upstash/vector SDK.
| Endpoint | Status |
|---|---|
POST /upsert[/{namespace}] |
Supported |
POST /query[/{namespace}] |
Supported (KNN + metadata filtering) |
POST /fetch[/{namespace}] |
Supported (by IDs and prefix) |
POST /delete[/{namespace}] |
Supported (by IDs, prefix, or filter) |
POST /update[/{namespace}] |
Supported (OVERWRITE and PATCH modes) |
POST /range[/{namespace}] |
Supported (cursor pagination) |
DELETE /reset[/{namespace}] |
Supported (single or all namespaces) |
GET /info |
Supported |
GET /list-namespaces |
Supported |
DELETE /delete-namespace/{ns} |
Supported |
Not implemented: /upsert-data, /query-data (require server-side embedding models), /resumable-query* (stateful cursors).
Full support for the Upstash filter syntax:
status = 'active' AND score >= 0.8
genre IN ('comedy', 'drama') AND year > 2020
tags CONTAINS 'featured'
geography.continent = 'Asia'
title GLOB 'The *' OR (rating >= 4.5 AND reviews > 100)
HAS FIELD premium
All operators: =, !=, <, <=, >, >=, GLOB, NOT GLOB, IN, NOT IN, CONTAINS, NOT CONTAINS, HAS FIELD, HAS NOT FIELD, AND, OR, parentheses, dot notation, array indexing.
Good fit if you:
- Want self-hosted vector search with zero vendor lock-in
- Run RAG workloads with topK 5-20 and under 100K vectors
- Already run Redis Stack (or want a single Docker Compose setup)
- Want the
@upstash/vectorSDK API without a cloud dependency
Use Upstash Cloud instead if you need:
- Server-side embeddings (built-in embedding models)
- Sparse or hybrid vector search
- DiskANN-level scale (millions of vectors)
- Managed infrastructure with zero ops
| Aspect | Upstash | up-vector |
|---|---|---|
| ANN algorithm | DiskANN | HNSW (RediSearch) |
| Metadata filtering | Server-side | App-level (over-fetch + filter) |
| Embedding endpoints | Built-in models | Not supported — bring your own vectors |
For RAG workloads with topK of 5-20 and <100K vectors, the differences are negligible.
All environment variables are prefixed UPVECTOR_:
| Variable | Default | Description |
|---|---|---|
UPVECTOR_TOKEN |
— | Required. Bearer token for API authentication |
UPVECTOR_REDIS_URL |
redis://localhost:6379 |
Redis Stack connection URL |
UPVECTOR_PORT |
8080 |
HTTP listen port |
UPVECTOR_HOST |
0.0.0.0 |
HTTP listen host |
UPVECTOR_METRIC |
COSINE |
Distance metric: COSINE, EUCLIDEAN, DOT_PRODUCT |
UPVECTOR_DIMENSION |
auto-detected | Fixed vector dimension (auto-detected from first upsert if omitted) |
UPVECTOR_LOG_LEVEL |
info |
Log level: debug, info, warn, error |
UPVECTOR_LOG_FORMAT |
json |
Log format: json (structured) or text (human-readable) |
UPVECTOR_SHUTDOWN_TIMEOUT |
30000 |
Max milliseconds to wait for request drain on shutdown |
UPVECTOR_REQUEST_TIMEOUT |
30000 |
Per-request timeout in milliseconds (0 = disabled) |
UPVECTOR_METRICS |
false |
Enable Prometheus metrics at GET /metrics |
Health check — no auth required:
# Lightweight probe (used by Docker HEALTHCHECK)
curl http://localhost:8080/
# → 200 "OK" or 503 "Shutting Down"
# Rich health endpoint with dependency status
curl http://localhost:8080/health
# → {"status":"ok","redis":"connected"}
# → {"status":"degraded","redis":"disconnected"} (503)
# → {"status":"shutting_down","redis":"..."} (503)Prometheus metrics — enable with UPVECTOR_METRICS=true:
curl http://localhost:8080/metricsExposes http_requests_total{method,status}, http_request_duration_seconds histogram, and upvector_info gauge in Prometheus exposition format.
Structured logging — JSON by default (set UPVECTOR_LOG_FORMAT=text for dev). Includes request IDs (X-Request-ID header), method, path, status, and duration for every request.
@upstash/vector SDK ──HTTP REST──▶ up-vector (Hono/Bun) ──Redis protocol──▶ Redis Stack (RediSearch)
- Runtime: Bun — native TypeScript, fastest JS runtime
- HTTP: Hono v4 — lightweight, fast
- Redis: Bun.redis (native, zero-dep) —
send()for rawFT.*RediSearch commands - Validation: Zod v3 — request body validation
Key design decisions: lazy index creation on first upsert, dimension auto-detection, namespace isolation via Redis key prefixes, app-level metadata filtering with over-fetch strategy, score normalization to Upstash's 0-1 range.
See PLAN.md for full architecture details and project structure.
bun install # Install dependencies
bun run dev # Dev server with --watch
bun run build # Bundle to dist/index.js
bun run lint # Biome check
bun run lint:fix # Biome auto-fix
bun run typecheck # TypeScript check198 tests across three tiers:
| Tier | Tests | Purpose |
|---|---|---|
| Unit | 110 | Filter parser, vector encode/decode, score normalization, key naming |
| Integration | 23 | End-to-end against Redis Stack |
| SDK Compatibility | 65 | Real @upstash/vector SDK against up-vector |
./scripts/test-all.sh # Run everything (starts Redis + server automatically)The compatibility tests are the ultimate validation — they use the actual @upstash/vector TypeScript SDK, exercising the exact HTTP paths and request formats that production apps use. A weekly CI job also tests against the latest SDK version to catch incompatibilities early.
See CONTRIBUTING.md for development setup and contribution guidelines.
cp .env.example .env # Set UPVECTOR_TOKEN
docker compose up -d # Starts up-vector + Redis StackBoth services can share the same Redis Stack instance — up-redis handles standard Redis commands, up-vector handles vector search:
services:
redis-stack:
image: redis/redis-stack-server:latest
up-redis:
image: ghcr.io/coriou/up-redis:latest
environment:
UPREDIS_TOKEN: ${UPREDIS_TOKEN}
UPREDIS_REDIS_URL: redis://redis-stack:6379
up-vector:
image: ghcr.io/coriou/up-vector:latest
environment:
UPVECTOR_TOKEN: ${UPVECTOR_TOKEN}
UPVECTOR_REDIS_URL: redis://redis-stack:6379MIT