# Getting started > Ragmux is a single-binary AI gateway written in Go. Source: https://ragmux.com/docs/getting-started/ Updated: 2026-10-11 Project: https://github.com/ragmux/ragmux (AGPL-3.0-or-later) --- Ragmux is a single-binary AI gateway written in Go. It sits between your applications and LLM providers, exposes one **OpenAI-compatible API**, and can augment every request with **retrieval (RAG)** from documents you upload. All state — users, model connections, projects, documents, chunks, vectors and metrics — lives in one **PostgreSQL** database with the [pgvector](https://github.com/pgvector/pgvector) extension. One container, no Redis, no separate vector database. ``` client ──► POST /v1/chat/completions (Bearer sk-proj-…) │ ├─ project → model connection (provider credentials stay server-side) ├─ project → RAG store (optional): embed query → top-k chunks → inject context └─ adapter: OpenAI · Anthropic · Gemini · DeepSeek · Ollama · custom OpenAI (vLLM…) JSON and SSE streaming, normalised to the OpenAI schema │ └─ PostgreSQL + pgvector: users, connections (AES-256-GCM keys), projects, documents (bytea), chunks, HNSW vector indexes, request logs ``` ## Features - **Providers:** `openai`, `azure_openai`, `anthropic`, `gemini`, `deepseek`, `ollama` (native `/api/chat`) and `custom_openai` for vLLM, LM Studio, LiteLLM or any OpenAI-compatible server, plus `cohere_rerank` and `voyage_rerank` for reranking. Ready-made presets cover Mistral, xAI and Groq; see [Providers](/docs/providers/). - **OpenAI-compatible proxy:** `/v1/chat/completions` (JSON + SSE streaming) and `/v1/models`; works with the official SDKs by changing `base_url` and `api_key`. Anthropic and Gemini requests and streams are translated, tool calls included, and remote images are fetched for the providers that cannot follow a URL themselves. - **More `/v1` endpoints:** `POST /v1/embeddings`, `POST /v1/retrieve` (search a RAG store without calling a model) and `POST /v1/rerank` (Cohere-shaped), each behind its own key scope. See the [API reference](/docs/api/#client-api-v1). - **Routes, retry and fallback:** a project can define named routes whose targets are ordered by priority and weight; the request's `model` then selects a route, and an unknown name is `404 model_not_found`. A chat call that fails before the first byte is retried or handed to the next target, and a connection that keeps failing is taken out of rotation by a circuit breaker. See [Routing](/docs/providers/#routing) and [Retry and fallback](/docs/providers/#retry-and-fallback). - **RAG stores:** PDF / DOCX / HTML / TXT / Markdown, section-aware chunking with contextual embeddings, hybrid search fusing pgvector with either Postgres full-text ranking or ParadeDB BM25, reranking by your own model or the Cohere / Voyage APIs, distance threshold. - **Projects:** one `sk-proj-…` key per project mapped to a model connection, a system prompt and an optional RAG store; provider credentials are encrypted at rest and never exposed. - **API keys:** per-user `sk-user-…` keys for `/v1` scoped to the projects you grant them and `sk-mgmt-…` keys for the management API, both with scopes (`/v1` keys: `chat`, `models`, `embeddings`, `retrieve`, `rerank`), expiry, revocation and optional limits under the project's. - **Users and roles:** `admin`, `editor`, `viewer`, project membership, login rate limiting with lockout, audit log of every management action, and `ragmux reset-password` when the last administrator is locked out. - **Cost:** prompt-caching passthrough, cache-aware token accounting and an editable price table that estimates what each request cost. - **Rate limits and budgets:** per-project requests / tokens per minute and daily / monthly token budgets shared across replicas, OpenAI-style `429` and `x-ratelimit-*` headers. - **Observability:** per-request logs (tokens, latency, status, streaming, RAG use, cost), summaries, daily series, configurable retention, an authenticated Prometheus `/metrics` endpoint and OpenTelemetry traces over OTLP (HTTP/JSON, or gRPC on request) with GenAI span names and one child span per upstream attempt. - **Scale:** run several replicas against one PostgreSQL — ingestion is claimed under a lease, the retention pass elects a leader, and streaming needs no session affinity. - **Dashboard:** embedded UI at `/admin/` for models, RAG stores, documents, projects and their routes, API keys, metrics, users, audit log and a playground; everything is also a REST API. ## Quick start Nothing to clone — one container with the gateway and its own PostgreSQL 17 + pgvector server inside: ```bash docker run -d --name ragmux \ -p 127.0.0.1:8765:8765 \ -e SECRET_KEY="$(openssl rand -hex 32)" \ -v ragmux-data:/data \ ragmux/ragmux:latest ``` Open . On a fresh database the dashboard asks you to **create the first administrator** (username and a password of at least 12 characters); that form only works while no user exists. For unattended installs pass `-e ADMIN_USER=admin -e ADMIN_PASSWORD=...` instead and the account is created on first start. `ADMIN_PASSWORD` follows the same rule (at least 12 characters, at most 72 bytes); on an empty database a shorter one stops the start with an error (see [Configuration](/docs/configuration/#environment-variables)). Two things worth knowing about that command: - **`SECRET_KEY` encrypts the provider credentials.** Nothing is required to start, but without it the gateway generates `/data/ragmux/secret.key` inside the volume, which you then have to back up together with the database. Keep the key with your backups. - **The port is published on loopback only.** Put a TLS-terminating reverse proxy in front for network access and set `TRUST_PROXY_HEADERS` / `SECURE_COOKIES`; see [Configuration](/docs/configuration/#behind-a-reverse-proxy). Change the mapping to `-p 8765:8765` only deliberately. Everything persistent is under `/data`: `/data/pg` is the PostgreSQL cluster (`PGDATA`) and `/data/ragmux` holds the `secret.key` fallback (`DATA_DIR`). That volume plus `SECRET_KEY` is the whole state. The embedded PostgreSQL has no TCP listener at all, so `8765` is the only port. Running models locally? Provider URLs on private networks (Ollama on the Docker host, a vLLM service on the same Docker network) are refused by default as an SSRF guard. Allow them by hostname before you add the connection — add `-e PRIVATE_UPSTREAM_ALLOWLIST=host.docker.internal,ollama` to the command above, or put the same variable in `.env` under Compose (or `ALLOW_PRIVATE_UPSTREAMS=true` on a trusted network — see [Configuration](/docs/configuration/#private-upstreams)). ### Which image Three variants are published from every release: | Tag | What it is | Choose it when | |---|---|---| | `ragmux/ragmux:latest` | **All-in-one:** the gateway plus its own PostgreSQL 17 + pgvector in one container, supervised by a single entrypoint (`Dockerfile.aio`). | You want one container and no database to operate. The default, and the right answer for most installations. | | `ragmux/ragmux:latest-app` | **Gateway only**, on a distroless base, with no database inside; you point it at your own PostgreSQL with `DATABASE_URL` (`Dockerfile`). | You already run PostgreSQL (managed service, your own cluster, a separate container), or you want **several gateway replicas** against one database — the all-in-one image cannot be scaled that way. | | `ragmux/ragmux:latest-paradedb` | **All-in-one on ParadeDB:** same gateway and same data layout, but its PostgreSQL carries `pg_search` (BM25) as well as `pgvector` (`Dockerfile.aio.paradedb`). | You want the lexical half of hybrid retrieval answered by **BM25** instead of PostgreSQL full-text ranking. Swapping from the default all-in-one image needs no reprocessing. | The `-paradedb` image takes exactly the same command and the same volume layout as the default one. Docker Hub is the primary registry; the full tag matrix, the GHCR mirror and the platforms are listed under [Published images](/docs/configuration/#published-images). **`latest` is for trying it out; pin a version in production.** The commands here use `latest` so they keep working as you read them, but a deployment you care about should name the release it was tested against (`ragmux/ragmux:`), or better the manifest digest that `cosign verify` reports (`ragmux/ragmux@sha256:…`), which no later tag move can change. Images are signed from v0.3.1 onward; see [Verifying the container image](/docs/security/#verifying-the-container-image). ### Docker Compose Compose is the second way in, and the better one once you want more than the single container gives you: **your own PostgreSQL**, the **split layout** (the gateway next to a `pgvector/pgvector:pg17` service you can back up and upgrade on its own schedule), the **ParadeDB** variant, a **multi-replica** setup behind a load balancer, or the scheduled **backup** profile and the `scripts/` helpers. It works from a clone of the repository: ```bash git clone https://github.com/ragmux/ragmux.git cd ragmux cp .env.example .env echo "SECRET_KEY=$(openssl rand -hex 32)" >> .env # keep it with your backups docker compose up -d ``` That is the same all-in-one image and the same `/data` volume as the `docker run` above, with `.env` as the one place configuration lives. The other files layer on top: | File | What it runs | |---|---| | [`docker-compose.yml`](https://github.com/ragmux/ragmux/blob/main/docker-compose.yml) | the default all-in-one container, plus a `backup` profile | | [`docker-compose.split.yml`](https://github.com/ragmux/ragmux/blob/main/docker-compose.split.yml) | the gateway (`-app`) next to its own `pgvector/pgvector:pg17`; needs `SECRET_KEY`, `POSTGRES_PASSWORD` and `RAGMUX_DB_PASSWORD` in `.env` | | [`docker-compose.paradedb.yml`](https://github.com/ragmux/ragmux/blob/main/docker-compose.paradedb.yml) | the split layout with ParadeDB in place of pgvector, for BM25 lexical retrieval | | [`docker-compose.scale.yml`](https://github.com/ragmux/ragmux/blob/main/docker-compose.scale.yml) | an overlay on the split file: several gateway replicas behind nginx — see [Scaling](/docs/scaling/) | | [`docker-compose.otel.yml`](https://github.com/ragmux/ragmux/blob/main/docker-compose.otel.yml) | an overlay adding an OpenTelemetry Collector — see [Observability](/docs/observability/) | For a PostgreSQL you already operate, set `DATABASE_URL` and the default container skips its embedded server, or run the gateway-only `-app` image with `DATABASE_URL` and `SECRET_KEY`. The two layouts, the `/data` volume and how to move between them are described in [Deployment layouts](/docs/configuration/#deployment-layouts). **Where state lives.** With the default layout everything is in the `ragmux-data` volume (`/data/pg`: the database, `/data/ragmux`: the `secret.key` fallback) plus `SECRET_KEY`. With the split layout the gateway container is stateless and the state is the `pgdata` volume plus `SECRET_KEY`. Either way, back up with `scripts/backup.sh` (`pg_dump -Fc`, works with both layouts) or the scheduled `backup` Compose profile — see [Backup and restore](/docs/backup-restore/). ## Using the gateway **1. Log in** and keep the session token: ```bash TOKEN=$(curl -s localhost:8765/admin/api/login -H 'Content-Type: application/json' \ -d '{"username":"admin","password":"YOUR_PASSWORD","bearer":true}' | jq -r .token) AUTH="Authorization: Bearer $TOKEN" ``` **2. Add model connections** (a chat model and, for RAG, an embedding model): ```bash curl -s localhost:8765/admin/api/models -H "$AUTH" -H 'Content-Type: application/json' -d '{ "name": "claude", "provider_type": "anthropic", "api_key": "sk-ant-...", "model_name": "claude-sonnet-4-5"}' curl -s localhost:8765/admin/api/models -H "$AUTH" -H 'Content-Type: application/json' -d '{ "name": "openai-embed", "provider_type": "openai", "api_key": "sk-...", "model_name": "text-embedding-3-small"}' ``` **3. Create a RAG store and upload documents** (optional): ```bash curl -s localhost:8765/admin/api/rag-stores -H "$AUTH" -H 'Content-Type: application/json' -d '{ "name": "handbook", "embedding_connection_id": 2}' curl -s localhost:8765/admin/api/rag-stores/1/documents -H "$AUTH" -F file=@handbook.pdf curl -s localhost:8765/admin/api/rag-stores/1/documents -H "$AUTH" # status: pending → processing → ready ``` **4. Create a project** and get its `sk-proj-…` key (shown only once): ```bash curl -s localhost:8765/admin/api/projects -H "$AUTH" -H 'Content-Type: application/json' -d '{ "name": "support-bot", "model_connection_id": 1, "rag_store_id": 1, "system_prompt": "You are the company support assistant."}' ``` **5. Call it like OpenAI.** The `model` field is echoed back but the project decides the real model (a project with [routes](/docs/providers/#routing) uses it to pick one); retrieved passages are injected into the system prompt and counted in the `x-ragmux-rag-hits` header. ```bash curl -N localhost:8765/v1/chat/completions \ -H "Authorization: Bearer sk-proj-..." -H 'Content-Type: application/json' \ -d '{"model":"default","stream":true,"messages":[{"role":"user","content":"How many vacation days do I get?"}]}' ``` ```python from openai import OpenAI client = OpenAI(base_url="http://localhost:8765/v1", api_key="sk-proj-...") resp = client.chat.completions.create(model="default", stream=True, messages=[{"role": "user", "content": "How many vacation days do I get?"}]) for chunk in resp: # Guard the array: an upstream may send a frame of its own with no # choices (a content filter result, a proxy keep-alive), and asking for # usage adds a final chunk that carries only the token counts. if chunk.choices: print(chunk.choices[0].delta.content or "", end="") ``` ## Dashboard `/admin/` serves a single-page dashboard (vanilla JavaScript, no build step, embedded in the binary) over the same REST API. On a fresh database it shows the first-run setup form; afterwards a sign-in screen that reports remaining attempts and lockouts. Screens: - **Overview:** 24h / 7d / 30d window with deltas against the previous period, requests per day, traffic by project, budget watch with exhaustion forecasts, recent requests and a CSV export. - **Models:** connection cards with provider capabilities, last test result and private upstream flag; the form can test a connection before saving it, start from a preset and takes an Azure OpenAI endpoint, deployment and API version. - **RAG stores:** quota meters, drag-and-drop upload, ingestion progress, per-document reprocess and a retrieval tester (hybrid / vector, rerank, per-stage latencies); the rerank connection list follows the chosen backend. - **Projects:** usage rings for the minute, day and month windows with reset countdowns, budget forecast banner, members, key rotation, a route editor (kind, up to eight targets with model override, weight and priority) and a project CSV export. - **API keys:** mint and revoke your own `sk-user-…` and `sk-mgmt-…` keys with a scope checklist, grants and limits. - **Playground:** real `/v1/chat/completions` calls with a project key, streaming or JSON, optional extra system prompt, RAG sources as citations and the response headers. - **Users** and **Audit log** (admins): role and failed-login tiles, filters, NDJSON export. What a user sees follows their role: viewers get read-only screens, editors the forms, admins the user and audit screens. The page ships its own fonts (Bricolage Grotesque, IBM Plex Sans, JetBrains Mono, all SIL OFL, `web/fonts/`) and runs under a strict `Content-Security-Policy`, so it makes no third-party requests. ## Roadmap Not in this release: an import tool for 0.1 (pre-PostgreSQL) databases, OCR for scanned PDFs, SSO / OIDC login, cost-denominated budgets, and a cache breakpoint on the gateway's own system prompt and RAG context block. The direction after this release is in [ROADMAP.md](https://github.com/ragmux/ragmux/blob/main/ROADMAP.md); to help, see [CONTRIBUTING.md](https://github.com/ragmux/ragmux/blob/main/CONTRIBUTING.md). ## License Ragmux is free software licensed under the **GNU Affero General Public License v3.0 or later** (AGPL-3.0-or-later); see [LICENSE](https://github.com/ragmux/ragmux/blob/main/LICENSE). The AGPL's network-service clause applies: if you run a modified version as a service that users interact with over a network, you must offer those users the corresponding source of your modified version.