Meilisearch: The Open-Source Search Engine That Makes Algolia's Invoice Disappear
"Every search box is a confession of intent. Meilisearch lets you own both the box and the confessions."
Adding good search to an application has traditionally meant choosing between two bad options: wire up
LIKE '%query%' and watch it die at 100,000 rows, or stand up Elasticsearch and inherit a JVM, a cluster, and a part-time operations job. The third option — a hosted search API like Algolia — works beautifully right up until the invoice arrives, because pricing scales with searches and records, and there is no self-hosting escape hatch.
Meilisearch (GitHub:
meilisearch/meilisearch) is the open-source answer to that triangle. Written in
Rust,
MIT-licensed, and carrying roughly
55,000 stars as of August 2026, it ships as a single binary or one container that delivers typo-tolerant, sub-50ms search with essentially zero configuration. For a blog about software you run on your own hardware, it's a particularly clean fit: search is the one feature where "just use a hosted API" is both expensive and a quiet privacy leak.
This is the honest breakdown — why it feels instant, how the ranking actually works, what self-hosting costs, and where the ceiling is before you should reach for Elasticsearch instead.
1. What Meilisearch Is (and Isn't)
Meilisearch is a
search engine you run yourself. You push JSON documents to it over a REST API, and it returns relevance-ranked results in milliseconds, with typo tolerance, faceted filtering, sorting, and geo-search all working immediately.
What it is
not is an analytics platform. Elasticsearch and OpenSearch grew into log aggregation and business intelligence, and their complexity reflects that. Meilisearch deliberately does one thing:
make a search box good. That narrowness is why a fresh index returns sensible results on the first query — which, if you've ever tuned Elasticsearch relevance from scratch, is not a small thing.
The trade-off for that focus is real and we'll cover it: the ceiling on custom relevance sits lower than engines where ranking is programmable rather than ordered. Meilisearch is betting that most applications need great defaults more than they need infinite tunability. For most applications, that bet is correct.
2. Why It Feels Instant
Three engineering choices explain the experience.
Rust. Memory safety without garbage-collection pauses, and a single binary with no runtime dependencies. It starts in seconds and there is no JVM to tune.
LMDB storage. Meilisearch uses Lightning Memory-Mapped Database, which memory-maps the index from disk and lets the operating system's page cache decide what's hot. The practical consequence: a 50 GB index does
not require 50 GB of RAM. Memory scales with the working set, not the total corpus. Cold reads incur disk I/O, but for typical workloads responses stay under 50ms. Contrast this with engines that keep the entire index resident in memory — faster theoretical reads, but your RAM bill scales with your data.
Prefix matching by default. Search-as-you-type works without building a separate autocomplete index. As a user types "sho", results for "shoes" appear. That single default is the difference between a search box that feels alive and one that feels like a form submission.
3. Typo Tolerance and the Ranking Rules
Meilisearch ships a default ranking pipeline applied in a fixed order:
1.
Words — how many query terms appear in the document.
2.
Typo — fewer typos rank higher.
3.
Proximity — matched words closer together rank higher.
4.
Attribute — matches in more important fields rank higher.
5.
Sort — any custom sort you've defined.
6.
Exactness — exact matches beat partial ones.
Typo tolerance scales with word length rather than applying a flat edit distance: 1–4 characters get none, 5–8 characters allow one typo, 9+ allow two. That's configurable per index, and worth disabling for SKUs and serial numbers where a "typo" is a different product entirely.
The important nuance: these rules are
ordered, not programmable. You can reorder, add, or remove them, but you can't write arbitrary scoring functions the way you can with Elasticsearch's query DSL or Vespa's ranking expressions. For 90% of applications the defaults are better than what a team would build themselves in a week. For the other 10%, that's the ceiling.
4. The Privacy Argument: Queries Are Intent Data
Here's the part that gets overlooked when people compare Meilisearch to Algolia on price alone.
Search queries are among the highest-signal behavioral data a product generates. They tell you what users want before they've decided, what they're confused about, what they're comparing, and what they almost bought. Sending your search traffic to a third-party API means that stream of intent data leaves your infrastructure — and for regulated industries, data sovereignty requirements may prohibit it outright. Hosted search providers also have
no self-hosting option, which means the architectural decision is permanent rather than incremental.
Self-hosting Meilisearch inverts all of it. Queries never leave your network. There's no per-search metering, so you can stop rationing features to control cost. And because it's MIT-licensed, there are fewer questions in legal review than GPL-licensed alternatives raise when a commercial product embeds a search engine.
5. Vector and Hybrid Search
Meilisearch added native vector and hybrid search in the v1.10+ line, combining traditional lexical matching with semantic retrieval. Worth being precise about how it works: Meilisearch calls an
external embedder rather than bundling embedding models. You point it at an embedding provider — which can be a commercial API, or your own local model server.
That design cuts both ways. It keeps the engine lightweight and lets you swap embedding providers, but it means "fully local semantic search" requires you to run an embedding service yourself (Ollama or similar) rather than getting it in the box. Competitors that bundle embedding models are simpler to set up; Meilisearch gives you more control over where inference happens. Given this blog's priorities, that's the right trade — but it's more work, and you should know it going in.
6. The Cost, Honestly
Software: $0, MIT. Self-hosted has unlimited documents, unlimited searches, and all core features. No usage metering at all.
Hosting: genuinely light by search-engine standards. Minimum around 512MB–1GB RAM for small indexes; a $10–$20/month VPS handles a substantial personal or small-business corpus. No separate JVM, no cluster coordination.
Storage: the LMDB index plus your data. Roughly proportional to corpus size; dramatically smaller than an equivalent Elasticsearch deployment.
Your time: the standout metric. Getting a working, good-feeling search box is an afternoon, not a sprint. This is where Meilisearch's total cost of ownership crushes Elasticsearch for small and mid-size projects.
Managed alternative: Meilisearch Cloud starts around
$20–30/month if you'd rather not own the disk and the backups.
Against Algolia's usage-based pricing — where costs scale with both searches and records, and premium features like semantic search sit behind the highest tiers — the saving at scale is substantial. But the stronger argument is the one in section 4: you stop exporting intent data as a condition of having a search box.
7. Honest Limitations
- Scaling ceiling. Past roughly tens of millions of documents, Elasticsearch or OpenSearch is the more proven answer. Meilisearch's single-writer design and limited horizontal scaling story make very large corpora a poor fit.
- Asynchronous indexing. Writes return a task ID and complete later; you poll for status. There is no write-then-read consistency, which catches teams out during first integration.
- Ranking is ordered, not programmable. Great defaults, lower ceiling for custom relevance than Elasticsearch or Vespa.
- No built-in analytics dashboard. Hosted competitors tell you what people searched for; self-hosted Meilisearch doesn't, unless you build it.
- Semantic search needs an external embedder. Not bundled, so "local AI search" means running your own embedding service.
- Heavy sustained write throughput on a large index will make the single-writer design felt.
None of these are surprising once stated, and for most projects none of them apply. They matter at the edges — which is exactly when you'd want to know.
8. Getting Started
``
bash
docker run -d \
--name meilisearch \
--restart unless-stopped \
-p 7700:7700 \
-v $(pwd)/meili_data:/meili_data \
-e MEILI_MASTER_KEY='your-secure-master-key' \
getmeili/meilisearch:latest
`
Then: (1) always set a master key — an exposed instance with no key is an open read/write index, (2) create an index and push documents, (3) declare which attributes are searchable, filterable, and sortable, (4) test typo tolerance with deliberately misspelled queries, (5) put authentication or a proxy in front before exposing it publicly. The master key is not optional; treat it like a database password.
`
bash
add documents
curl -X POST 'http://localhost:7700/indexes/books/documents' \
-H "Authorization: Bearer $MEILI_MASTER_KEY" \
-H 'Content-Type: application/json' \
--data-binary @books.json
`
9. Meilisearch vs the Alternatives
| | Meilisearch | Typesense | Elasticsearch | Algolia |
|---|---|---|---|---|
| License | MIT | GPLv3 | varies | proprietary |
| Language | Rust | C++ | Java | — |
| Storage | LMDB (disk-backed) | in-memory | disk + JVM | hosted |
| Self-host | ✅ | ✅ | ✅ | ❌ |
| Typo tolerance | ✅ zero-config | ✅ | manual | ✅ |
| CJK / Arabic support | ✅ strong | limited | ✅ | ✅ |
| Built-in semantic models | ❌ (external) | ✅ | via plugins | ✅ |
| Ops burden | low | low | high | none |
| RAM vs corpus | scales with working set | full index in RAM | high | N/A |
The short version: Meilisearch is the pick for the smoothest path to a working search box, strong multilingual support, and MIT licensing. Typesense is excellent if you want built-in embedding models or are porting an Algolia front end. Elasticsearch/OpenSearch wins above tens of millions of documents or when you need analytics. Algolia wins on polish and loses on cost, lock-in, and data sovereignty.
10. Who Should Run It
Run it if: you have an application whose search is currently a database LIKE
query, you're paying a hosted search bill you resent, you need multilingual search including CJK, you want to stop shipping query data to a third party, or you simply want a search box that works this afternoon.
Skip it if: your corpus is in the tens of millions of documents and climbing (use Elasticsearch), you need programmable relevance scoring (Vespa or Elasticsearch), you want logs and analytics more than search, or you need built-in semantic search with zero setup (Typesense bundles embeddings).
For this blog's readers — running Immich, Paperless NGX, Supabase, and a Docker host — Meilisearch is the missing layer that makes all of it findable. Your archive is only as useful as your ability to search it, and search is the one feature where "rent it" costs both money and privacy.
11. A Real Deployment Walkthrough
Step 1 — set the master key and never expose the raw port. Use a key, and put a reverse proxy with TLS in front. An unprotected Meilisearch instance is an open index.
Step 2 — design your attributes before bulk indexing. Decide searchableAttributes
, filterableAttributes
, and sortableAttributes
up front. Changing them on a large index means reindexing, and that's the most common avoidable reindex in practice.
Step 3 — index a sample first. Push a few hundred documents, confirm typo tolerance and ranking behave as expected, and only then load the full corpus.
Step 4 — handle the async write model. In application code, treat indexing as a task: submit, then poll the task status if you need confirmation. Don't write and immediately query expecting the new document to appear.
Step 5 — plan for index updates. Reindexing is normal when settings change. Automate it rather than doing it by hand, and version your settings alongside your code.
12. Troubleshooting & Tuning
- New documents don't appear in search immediately — expected: indexing is asynchronous. Poll the task status.
- "Missing master key" warnings — you didn't set
MEILI_MASTER_KEY
. In production, refusing to start without one is a feature, not a bug.
Changing filterable attributes does nothing until reindex — by design. Trigger a full reindex after settings changes.
Memory grows with the corpus — LMDB maps the index; ensure the container has enough headroom, and monitor disk alongside RAM.
Typo tolerance produces wrong matches for SKUs — disable typo tolerance on those specific fields.
Non-Latin search feels off — confirm you're on a build with your language's segmentation support; Meilisearch handles CJK, Arabic, and Thai natively, but only if configured.
13. Index Design: Attributes Are the Whole Game
If there's one thing to get right early, it's attribute configuration. Meilisearch needs to be told which fields are searchable, which are filterable, and which are sortable — and those decisions are baked into the index.
- Searchable attributes control both which fields are matched and their priority, because the attribute ranking rule uses the declared order. Put the title first and the body second if title matches should win.
- Filterable attributes enable faceting and filtering. Only fields declared filterable can be used in filter expressions, and only these produce facet counts.
- Sortable attributes enable result sorting at query time.
The trap: changing any of these on a populated index requires a reindex. That's not a Meilisearch flaw — it's inherent to how an inverted index is built — but teams routinely declare attributes hastily, load a million documents, then discover they need a new filterable field and have to rebuild everything.
So: decide your attributes before bulk indexing, and version those settings in code alongside your application. A settings file in your repository means a rebuild is a command, not an archaeology project. The good news is that reindexing Meilisearch is fast compared to heavier engines, so even a mistake is recoverable in minutes rather than hours.
14. Working With the Ranking Rules
Because ranking is ordered rather than programmable, tuning is mostly about ordering and field priority rather than writing scoring functions.
Start by reordering the default rules to match your domain. An e-commerce catalogue might want sort
(price, popularity) higher. A documentation site usually wants words
and proximity
to dominate so exact phrase matches beat keyword soup. You can also add custom rules for things like ascending date or descending popularity.
Beyond ordering, the highest-leverage knobs are:
- Synonyms — map equivalent terms so "sofa" finds "couch."
- Stop words — drop noise words per language, which matters more for some languages than others.
- Typo tolerance settings — disable on codes, SKUs, and serial numbers where one character is a different thing entirely.
- Searchable attribute order — the cheapest relevance win available, and the one people forget.
The honest framing: you will get better results from Meilisearch's defaults in an afternoon than from a week of Elasticsearch tuning, but you will eventually hit a wall where you want a scoring function you can't express. Knowing where that wall is — before you build on it — is the point.
15. Multi-Tenancy and Tenant Tokens
If you're serving search to more than one customer or one isolated user group, Meilisearch has a clean answer that avoids running a separate index per tenant: tenant tokens.
A tenant token is a signed JWT generated with your master (or an API) key, embedding search rules that restrict what the holder can see. You generate it server-side and hand it to the client; the client searches directly against Meilisearch, and the embedded rules are enforced on every query. From the application's perspective, the browser talks to the search engine — no proxy hop, no per-request server round trip — while still being unable to see anyone else's data.
`
js
// server-side: mint a token scoped to one tenant
const token = client.generateTenantToken(searchKey, {
apiKey: searchKey,
searchRules: { products: 'tenant_id = acme' }
}, { apiKey: searchKey, expiresAt: futureDate });
``
The security model is worth stating plainly, because it's the kind of thing people get wrong: tenant tokens are signed, not encrypted, and the rules travel with the token. That's fine — the rules are enforceable rather than secret — but it means you should scope them narrowly, give them short expiry windows, and never confuse them with the master key, which can do anything including delete indexes. Use tenant tokens for clients, keep API keys server-side, and rotate on a schedule. Done properly, this is one of the tidier multi-tenant search stories available in a self-hosted engine.
Related
Comments (0)
No comments yet. Be the first to comment!