Self-Hosting Typesense: A Fast, Typo-Tolerant Search Engine
Search is one of those features that's easy to build poorly and hard to build well. A SQL LIKE query doesn't handle typos, doesn't rank results intelligently, and slows down as your dataset grows. Elasticsearch handles all of that, but it's a distributed system that demands gigabytes of RAM and serious operational knowledge.
Typesense sits in the middle. It's a search engine purpose-built for speed and simplicity — written in C++ for raw performance, typo-tolerant by default, and deployable in minutes. It's the closest thing to "Algolia but self-hosted."
Why Typesense?
- Blazing fast — Written in C++, single-digit millisecond search responses
- Typo tolerance — Handles misspellings out of the box
- Schema-enforced — Define your data structure upfront, catch errors early
- Geo search — Built-in support for location-based search
- Clustering — High availability with built-in Raft consensus
- Curation — Pin or hide specific results for specific queries
- Low resources — Runs well on 512 MB of RAM for small datasets
The schema-enforced approach is the key philosophical difference from Meilisearch. Typesense makes you define your collection schema upfront (types for each field), which catches data issues early but requires more initial setup.
Docker Deployment
# docker-compose.yml
services:
typesense:
image: typesense/typesense:27.1
ports:
- "8108:8108"
volumes:
- typesense_data:/data
command: >
--data-dir /data
--api-key=your-admin-api-key
--enable-cors
restart: unless-stopped
volumes:
typesense_data:
docker compose up -d
Typesense is running at http://your-server:8108. The --api-key flag sets your admin API key.
Creating a Collection
Typesense requires you to define a schema before indexing documents. This is different from Meilisearch's schemaless approach.
curl 'http://localhost:8108/collections' \
-X POST \
-H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
-H 'Content-Type: application/json' \
-d '{
"name": "products",
"fields": [
{"name": "name", "type": "string"},
{"name": "description", "type": "string"},
{"name": "category", "type": "string", "facet": true},
{"name": "price", "type": "float"},
{"name": "rating", "type": "float", "optional": true},
{"name": "in_stock", "type": "bool", "facet": true}
],
"default_sorting_field": "rating"
}'
The facet: true flag enables filtering and faceted search on that field. The default_sorting_field determines how results are ranked when no explicit sort is specified.
Indexing Documents
curl 'http://localhost:8108/collections/products/documents' \
-X POST \
-H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
-H 'Content-Type: application/json' \
-d '{
"name": "Ergonomic Keyboard",
"description": "Split mechanical keyboard with Cherry MX switches",
"category": "Electronics",
"price": 149.99,
"rating": 4.7,
"in_stock": true
}'
For bulk indexing, use the import endpoint with JSONL format:
curl 'http://localhost:8108/collections/products/documents/import?action=upsert' \
-X POST \
-H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
-H 'Content-Type: text/plain' \
-d '{"name":"Ergonomic Keyboard","description":"Split mechanical keyboard","category":"Electronics","price":149.99,"rating":4.7,"in_stock":true}
{"name":"USB-C Monitor","description":"32-inch 4K display with USB-C","category":"Electronics","price":399.99,"rating":4.5,"in_stock":true}'
Searching
curl 'http://localhost:8108/collections/products/documents/search' \
-H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
-G \
-d 'q=ergnomic keybard' \
-d 'query_by=name,description' \
-d 'filter_by=category:=Electronics&&price:<200' \
-d 'sort_by=rating:desc'
The typos in "ergnomic keybard" are handled automatically. Typesense returns the "Ergonomic Keyboard" as expected.
Faceted search
Facets let you build filter menus (like "Category: Electronics (12), Office (8)"):
curl 'http://localhost:8108/collections/products/documents/search' \
-H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
-G \
-d 'q=*' \
-d 'query_by=name' \
-d 'facet_by=category,in_stock'
The response includes facet counts for each category and stock status — ready to render as filter checkboxes in your UI.
Scoped API Keys
Never expose your admin API key to the frontend. Create scoped search-only keys:
curl 'http://localhost:8108/keys' \
-X POST \
-H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
-H 'Content-Type: application/json' \
-d '{
"description": "Search-only key",
"actions": ["documents:search"],
"collections": ["products"]
}'
Use this key in your frontend JavaScript. It can only search, never modify data.
Instant Search UI
Typesense provides an InstantSearch adapter that works with Algolia's open-source InstantSearch.js library:
<script src="https://cdn.jsdelivr.net/npm/typesense-instantsearch-adapter/dist/typesense-instantsearch-adapter.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js"></script>
const typesenseAdapter = new TypesenseInstantSearchAdapter({
server: {
apiKey: 'your-search-only-key',
nodes: [{ host: 'search.yourdomain.com', port: 443, protocol: 'https' }]
},
additionalSearchParameters: {
query_by: 'name,description'
}
});
const search = instantsearch({
indexName: 'products',
searchClient: typesenseAdapter.searchClient
});
This gives you a polished search UI with as-you-type results, facet filters, and pagination without building it from scratch.
High Availability Clustering
For production use, Typesense supports a 3-node cluster with Raft consensus:
services:
typesense-1:
image: typesense/typesense:27.1
command: >
--data-dir /data
--api-key=your-admin-api-key
--nodes=/config/nodes.txt
--peering-address=typesense-1
--peering-port=8107
volumes:
- ts1_data:/data
- ./nodes.txt:/config/nodes.txt
typesense-2:
image: typesense/typesense:27.1
command: >
--data-dir /data
--api-key=your-admin-api-key
--nodes=/config/nodes.txt
--peering-address=typesense-2
--peering-port=8107
volumes:
- ts2_data:/data
- ./nodes.txt:/config/nodes.txt
Create nodes.txt:
typesense-1:8107:8108,typesense-2:8107:8108,typesense-3:8107:8108
Typesense vs Meilisearch vs Elasticsearch
| Feature | Typesense | Meilisearch | Elasticsearch |
|---|---|---|---|
| Language | C++ | Rust | Java |
| Schema | Required | Optional | Optional |
| Typo tolerance | Built-in | Built-in | Manual config |
| RAM usage (small) | ~100 MB | ~100 MB | ~2-4 GB |
| Clustering | Built-in (Raft) | Not yet | Built-in |
| Geo search | Yes | Yes | Yes |
| Faceted search | Yes | Yes | Yes |
| InstantSearch compat | Yes (adapter) | Yes (adapter) | Yes (native) |
| License | GPL-3.0 | MIT | SSPL |
When to choose Typesense
- You need high-availability clustering for search
- You prefer strict schemas that catch data issues early
- You want a drop-in Algolia replacement (InstantSearch compatible)
- You need the raw speed of a C++ engine
When to choose Meilisearch
- You want the simplest possible setup with minimal configuration
- You prefer schemaless ingestion for rapid prototyping
- You need an MIT-licensed solution
The Bottom Line
Typesense is the self-hosted search engine for teams that want production-grade reliability with minimal fuss. The schema-enforced approach catches errors before they reach your search index, the built-in clustering provides real high availability, and the InstantSearch compatibility means you can build a polished search UI in an afternoon. If you're currently paying for Algolia or fighting with Elasticsearch, Typesense gives you fast, typo-tolerant search that runs on your own infrastructure.