Open-source, self-hosted RAG infrastructure built for early-stage startups. High performance, low cost, and simple to run.
- Ingest documents from URLs, file uploads, or S3. RagPack handles fetching, parsing, chunking, and embedding automatically
- Semantic search: query a collection with natural language and get back ranked, scored chunks ready to use in any LLM prompt
- RAG: send a question with a prompt template and a model. RagPack retrieves the relevant chunks and returns a grounded answer
- Bring your own embedding model. Ollama or TEI for fully local inference, or any OpenAI-compatible provider
- Manage everything via REST API or the built-in admin UI
Supported formats: .txt, .md, .html, .pdf, .docx, .pptx, .xlsx, .csv, .json, .xml
Developers who want to add semantic search or RAG to an existing app without building and maintaining the infrastructure themselves. Designed to run on minimal infrastructure. Spin it up with npx ragpack start, then use the TypeScript SDK or REST API. No pipeline code, no boilerplate, no cloud bill.
Prerequisites: Docker must be installed and running.
Install the CLI:
npm install -g ragpackOr run without installing via npx:
npx ragpack initThen start the stack:
ragpack init # creates .env.ragpack in the current directory
ragpack start # starts the stack (API on :9000, admin UI on :3000)With Ollama (fully local):
npx ragpack start --profile ollamaWith OpenAI, edit .env.ragpack first:
EMBED_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_EMBED_MODEL=text-embedding-3-smallThen npx ragpack start.
The admin UI opens automatically at http://localhost:3000. Logs stream in the terminal. Ctrl+C stops following logs without stopping the stack.
For headless/VPS use, start in the background:
ragpack start --detach| Command | Description |
|---|---|
ragpack init |
Create .env.ragpack in the current directory |
ragpack start [--profile ollama] |
Start the stack (streams logs, opens admin UI) |
ragpack start --detach |
Start in background without following logs |
ragpack stop [-v] |
Stop the stack (-v removes volumes and all data) |
ragpack logs [service] |
Tail logs (backend, web-admin, ollama) |
ragpack update |
Pull latest images and restart |
ragpack eject |
Copy docker-compose.yml locally for customization |
| Layer | Technology |
|---|---|
| API server | Go + Fiber |
| Vector store | LanceDB (embedded) |
| Metadata / job state | SQLite |
| Embeddings | Ollama (local), OpenAI, or HuggingFace TEI |
| Admin UI | Next.js |
The stack is chosen for low memory footprint (~20MB idle), a single static binary with no runtime dependencies, and fast query performance. RagPack runs comfortably on a $5 VPS or a spare Mac Mini.
.env.ragpack (created by ragpack init):
# Embedding provider
EMBED_PROVIDER=ollama # ollama | openai | tei
# Ollama
OLLAMA_BASE_URL=http://ollama:11434
OLLAMA_EMBED_MODEL=nomic-embed-text
# OpenAI
OPENAI_API_KEY=sk-...
OPENAI_EMBED_MODEL=text-embedding-3-small
# HuggingFace TEI
TEI_EMBED_MODEL=BAAI/bge-small-en-v1.5
# Ingestion
WORKER_COUNT=5
EMBED_RATE_LIMIT=10 # max embed API calls/secStorage defaults to /data inside the container, backed by a named Docker volume.
npm install ragpack-jsimport { RagPack } from "ragpack-js";
const client = new RagPack({ baseUrl: "http://localhost:9000", apiKey: "rp_..." });
const collection = client.collection("my-docs");
// Ingest a file
await collection.ingest(file);
// Semantic search: returns ranked chunks (hybrid by default, with optional filters)
const results = await collection.findSimilar({
query: "what is RagPack?",
filters: { category: "docs" },
});
// RAG: retrieves chunks and returns an LLM answer
const { answer, chunks } = await collection.rag({
query: "How do I configure authentication?",
promptSlug: "basic-rag",
model: "gpt-4o",
});
// Documents: fetch, read typed metadata, or update name/extra_json/metadata
const doc = await collection.documents.get(docId);
const metadata = await collection.documents.metadata(docId);
await collection.documents.update(docId, { metadata: { status: "published" } });For the full SDK reference, see ragpack.dev/docs/sdk/js.
Base URL: http://localhost:9000/api/v1
All requests require an API key. On first startup the backend prints it to the logs and saves it to /data/api_key inside the container. To retrieve it:
ragpack logs backend | grep "Key:"Pass it as a bearer token in every request:
export RAGPACK_API_KEY=rp_...| Method | Path | Description |
|---|---|---|
GET |
/collections |
List collections |
POST |
/collections |
Create a collection |
GET |
/collections/:slug |
Get a collection |
DELETE |
/collections/:slug |
Delete a collection and all its data |
curl -X POST http://localhost:9000/api/v1/collections \
-H "Authorization: Bearer $RAGPACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My Docs"}'| Method | Path | Description |
|---|---|---|
POST |
/collections/:slug/ingest |
Ingest a URL or file upload |
# Ingest a URL
curl -X POST http://localhost:9000/api/v1/collections/my-docs/ingest \
-H "Authorization: Bearer $RAGPACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"file_uri": "https://example.com/docs/guide"}'
# Upload a file
curl -X POST http://localhost:9000/api/v1/collections/my-docs/ingest \
-H "Authorization: Bearer $RAGPACK_API_KEY" \
-F "file=@./document.pdf"Supported sources: https://, s3://, file uploads.
Supported formats: .txt, .md, .html, .pdf, .docx, .pptx, .xlsx, .csv, .json, .xml
| Method | Path | Description |
|---|---|---|
POST |
/collections/:slug/query |
Semantic or hybrid search, returns ranked chunks |
POST |
/collections/:slug/rag |
RAG, retrieves chunks and returns an LLM answer |
# Semantic search (hybrid by default — vector + keyword, merged with weighted RRF)
curl -X POST http://localhost:9000/api/v1/collections/my-docs/query \
-H "Authorization: Bearer $RAGPACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "how do I configure authentication?", "top_k": 5, "filters": {"category": "docs"}}'
# RAG
curl -X POST http://localhost:9000/api/v1/collections/my-docs/rag \
-H "Authorization: Bearer $RAGPACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "how do I configure authentication?", "top_k": 5, "prompt_slug": "my-prompt", "model": "llama3"}'The query response includes chunk_text, file_uri, vector_distance, and vector_similarity (0–100), plus keyword_bm25_score, rrf_score, and rrf_score_normalized when hybrid search ran. Pass vector_search_only: true to skip the keyword pass. The RAG response adds answer and formatted_prompt.
| Method | Path | Description |
|---|---|---|
GET |
/collections/:slug/documents |
List ingested documents (paginated) |
GET |
/collections/:slug/documents/:id |
Get a document |
GET |
/collections/:slug/documents/:id/metadata |
Get a document's typed metadata field values |
PATCH |
/collections/:slug/documents/:id |
Update name, extra_json, and/or metadata |
DELETE |
/collections/:slug/documents/:id |
Delete a document and its chunks |
GET |
/collections/:slug/documents/:id/chunks |
List all chunks |
| Method | Path | Description |
|---|---|---|
GET |
/keys |
List API keys |
POST |
/keys |
Create an API key |
DELETE |
/keys/:id |
Delete an API key |
Keys carry two independent, fail-closed grant kinds: per-collection grants (read/write/both, or wildcard by omitting collection_slug) and admin_grants for instance administration (keys/prompts/collections/*) — so a key that can manage prompts doesn't automatically get collection access, and vice versa. See the API reference for the full grant model and examples.
The admin UI at http://localhost:3000 lets you:
- Create and delete collections
- Ingest documents via URL or file upload
- Monitor ingestion status (ingesting / complete / failed)
- Delete individual documents
- Run queries against a collection, with hybrid search and filters
- Manage API keys and their collection/admin grants
Supported out of the box:
| Model | Dimensions | Provider |
|---|---|---|
nomic-embed-text |
768 | Ollama |
mxbai-embed-large |
1024 | Ollama |
bge-m3 |
1024 | Ollama |
all-minilm |
384 | Ollama |
text-embedding-3-small |
1536 | OpenAI |
text-embedding-3-large |
3072 | OpenAI |
text-embedding-ada-002 |
1536 | OpenAI |
To add a model, edit backend/pkg/embedder/dimensions.go.
- Markdown: splits on headers, prepending parent breadcrumbs to each chunk for context
- HTML: converted to Markdown (stripping nav, footer, scripts) then chunked as above
- PDF: text extracted page-by-page, chunked as plain text
- Plain text: sliding window (2000 chars, 200 char overlap)
Prerequisites: Docker
npm run dev # backend + admin UI with hot reload
npm run dev:ollama # include Ollama
npm run dev:tei # include HuggingFace TEIBackend runs on :9000, admin UI on :3000.
Apache-2.0