SereneDB Team
Aug 3, 2026 · 8 minutes read
Serene Docs Search: a small, open-source alternative to Algolia DocSearch
Point it at a website, repository, local folder or S3 bucket and get documentation search that you own
We needed search on our docs. We make a search database. You can see where this went.
docs.serenedb.com and this blog now run on SereneDB-backed search. Then we packaged the whole path from "these are my docs" to "there is a search box on the site" into Serene Docs Search.
The short version is: point it at a Git repository, a local folder, a live HTML site or an S3-compatible bucket; choose full-text or hybrid search; download the generated config and Compose file; add one React component or script tag to the site. It keeps the index in sync after that. Ask AI can answer questions with citations. An optional MCP server lets Codex, Claude and other agents search and read the same index.
For straightforward documentation and website search, it is a small self-hosted alternative to Algolia DocSearch. It is not trying to reproduce every feature of a search SaaS. It covers the common case, keeps the stack and data under your control and the application packages are MIT-licensed. Full-text mode does not need an embeddings service or a model API, so the only bill is the infrastructure you choose to run it on.
From source to search box
Documentation does not have one standard home. It may be Markdown in the main repository, generated HTML on a website, a directory mounted into a build server or exported files in object storage. Serene Docs Search treats all four as first-class sources:
- Git: follow a branch, pin a commit and limit indexing to selected files or subdirectories.
- Local folder: mount any directory read-only into the backend.
- Website: crawl from a URL, follow a sitemap and choose a maximum link depth.
- S3: index a bucket or prefix, including S3-compatible services such as R2 and MinIO through a custom endpoint.
The indexer understands Markdown, MDX, HTML, reStructuredText, plain text,
Jupyter notebooks and PDFs. Markdown can stay as one result per file or split at
headings so a result links directly to the relevant section. HTML extraction can
be scoped to article, main or your own CSS selectors, with navigation and
other boilerplate excluded before indexing.
Source paths and public URLs are often different, so URL mapping is part of the
configuration rather than application glue. You can strip source prefixes and
file extensions, map README and index files to directory URLs or route
different paths to different sites. One repository can serve a docs domain and a
blog domain without giving either one broken result links.
Git / folder / website / S3 ── pull, parse, sync ──┐
▼
browser widget ── /v1/search, /v1/ask ──> search backend ──> SereneDB
│
AI agents ── MCP tools ────────────────────────┘
The setup wizard asks for the source, parsing rules, search mode and sync policy,
then generates serene-search.config.json and docker-compose.yml using the same
code as the backend packages. The standard stack is two containers: SereneDB and
the search backend. Ollama and the MCP service appear only when you enable them.
You can configure a real stack from this page now. The wizard saves an unfinished draft in this browser, but it does not change the search configuration of this site.
Search first; AI stays optional
The default job of a docs search box is to find a page while somebody is still typing. Serene Docs Search starts there.
Full-text mode uses BM25 with extra weight on titles. All complete query terms
must match, while the last term works as a prefix for search-as-you-type. The
analyzer lowercases and folds accents, removes stop words and stems terms, so
run can find running. You can add Solr-format synonyms such as
db, database or k8s => kubernetes.
Relevance gets a few deliberately practical passes on top:
- exact and prefix title matches stay above looser matches;
- one long page cannot occupy the entire result list with its headings;
- configured pages can be pinned for matching queries;
- if a strict query returns nothing, a typo-tolerant pass tries again and can show a Did you mean correction;
- partial matches are used as a last fallback instead of returning an empty modal;
- every lexical hit includes a snippet with the matching terms highlighted.
If keyword search is enough, stop there. There are no vectors to generate and no model endpoint to operate.
Hybrid mode adds vector similarity over the same sections and merges the lexical and semantic rankings with reciprocal rank fusion. The embeddings provider can be OpenAI, Ollama or another OpenAI-compatible endpoint. The fusion weight, candidate window and distance threshold are tunable per corpus.
Search results can also be grouped into sections such as Docs, Blog and API reference. The group matching the page a visitor is currently reading moves first, while relevance order stays unchanged inside every group. Empty groups disappear. This is useful when one search backend serves several related sites without making users choose a scope before they search.
When a user selects a result, the widget navigates to the heading anchor, flashes the target heading and highlights the query terms on the destination page. Query and click counters are recorded too, with an admin-only report for top queries, zero-result queries and most-clicked pages.
Ask the same index a question
Ask AI is a separate switch, not a requirement for hybrid search. When it is enabled, the model searches and reads sections through tools over the same index, streams the answer into the modal and cites the pages it used. Multi-turn history stays in the conversation. Providers without tool calling fall back to a single retrieval-and-answer pass.
The answer model and embeddings model are configured independently. You can use an OpenAI-compatible API for one, local Ollama for the other, use Ollama for both, or leave Ask AI off completely. API keys stay in backend environment variables; they are never shipped with the browser widget.
The docs can be tools for agents too
A useful documentation index should not only serve humans. The optional
@serenedb/docs-search-mcp server exposes three tools:
search_docsfinds relevant sections;read_sectionreturns the full text behind one result;docs_healthreports whether the corpus is indexed and current.
It uses the same public HTTP API as the widget, so there is no second ingestion pipeline or agent-specific copy of the docs. The modal can show a ready-to-copy setup command for Codex or Claude. The configurator can also add the MCP container to the generated Compose stack. Ask AI does not depend on MCP; the two features remain independent opt-ins.
Keep it current without rebuilding everything
The backend owns synchronization as well as queries. For Git it can cheaply watch the branch for a new commit. Any source can be polled on an interval. CI can instead call the reindex webhook after a deployment.
Snapshots hash every parsed section. Unchanged sections are skipped, changed ones are replaced and sections deleted from the source are pruned from the index. The setup flow shows fetch, parse, embed and index progress during the initial build, and the health endpoint reports document counts and the last successful sync.
Administrative routes for configuration and manual sync are protected by a token that the Compose generator creates for you. Search, health and Ask AI remain public, so there is no admin secret in the website bundle.
Put the UI where you need it
For React, the complete integration is a component and a stylesheet:
npm install @serenedb/docs-search-react@latest
import { SereneDocsSearch } from "@serenedb/docs-search-react";
import "@serenedb/docs-search-react/styles.css";
<SereneDocsSearch backendUrl="https://search.example.com" />
The component includes the trigger, modal, keyboard navigation, Cmd/Ctrl+K
hotkey, automatic light/dark theme, recent queries, connection status, Search,
Ask AI and MCP tabs. It can hand navigation to an SPA router, use a custom
trigger, run as a controlled modal or render into a custom portal.
If you already have a search interface, useSereneDocsSearch() exposes the same
connection, query, grouping, navigation and streamed-answer state without our UI.
If you do not use React, the embed package bundles the widget behind a stylesheet
and one script tag, with both explicit and zero-JavaScript initialization.
Underneath those integrations is a small HTTP API for health, search, streamed answers, reading complete sections, synchronization and redacted configuration. The widget is optional; the index is usable from any client that can make an HTTP request.
What “free alternative” means
Serene Docs Search is deliberately for the simple case: a site or docs corpus, a search modal, a small backend and one database. If you need a global managed search platform with a large operations team behind it, use one. If you need good search over your own docs and would rather keep the source, index, relevance configuration and deployment in your own repository, this is the case we built for.
The software is open source and MIT-licensed. There is no per-search fee and full-text mode has no model cost. Self-hosting still means providing the machine, storage and operations. Optional model APIs charge whatever their providers charge. The trade is explicit: less SaaS surface area, more ownership.
That is also why the configurator generates plain files instead of creating a project in somebody else's control plane. You can inspect them, commit them, change them and deploy them anywhere Docker Compose runs.
Try the search on this site, open the complete configuration guide, or generate your own stack with Configure now above.