Self-Hosting Meilisearch: Lightning-Fast Full-Text Search for Your Apps
You've built an app with a search bar. Behind it sits a SQL LIKE '%query%' that takes 800ms, ignores typos, and returns results in whatever order the database feels like. Your users deserve better, but Elasticsearch requires a PhD in YAML configuration and 4 GB of RAM just to start.
Meilisearch is a search engine built for exactly this gap. It's fast (sub-50ms responses), typo-tolerant by default, and simple enough to deploy in five minutes. It's what search should feel like for small-to-medium applications.
Why Meilisearch?
Meilisearch occupies a specific niche in the search engine landscape:
- Instant results — Sub-50ms search responses out of the box
- Typo tolerance — Handles misspellings automatically, no configuration needed
- Simple API — RESTful interface that's genuinely easy to use
- Low resource usage — Runs comfortably on 512 MB of RAM for small datasets
- Built-in frontend tools — Official SDKs for JavaScript, Python, Ruby, Go, and more
The trade-offs: Meilisearch is not designed for log analytics or massive datasets (billions of documents). It's purpose-built for user-facing search — product catalogs, documentation, articles, and similar content.
Architecture Overview
Docker Deployment
# docker-compose.yml
services:
meilisearch:
image: getmeili/meilisearch:v1.12
ports:
- "7700:7700"
volumes:
- meili_data:/meili_data
environment:
MEILI_ENV: production
MEILI_MASTER_KEY: "your-secret-master-key-at-least-16-bytes"
MEILI_NO_ANALYTICS: "true"
restart: unless-stopped
volumes:
meili_data:
docker compose up -d
Meilisearch is now running at http://your-server:7700. The master key protects write access — without it, anyone can modify your indexes.
Important: In production mode (MEILI_ENV: production), a master key is required. Meilisearch generates API keys derived from the master key: a search-only key (safe to expose in frontends) and an admin key (for indexing operations).
Adding Data and Searching
Create an index and add documents
curl -X POST 'http://localhost:7700/indexes/products/documents' \
-H 'Authorization: Bearer your-master-key' \
-H 'Content-Type: application/json' \
--data-binary '[
{"id": 1, "name": "Mechanical Keyboard", "category": "Electronics", "price": 89.99},
{"id": 2, "name": "USB-C Hub", "category": "Electronics", "price": 34.99},
{"id": 3, "name": "Standing Desk Mat", "category": "Office", "price": 49.99}
]'
Search
curl 'http://localhost:7700/indexes/products/search' \
-H 'Authorization: Bearer your-search-key' \
-d '{"q": "mechancal keybord"}'
Notice the typos in the query. Meilisearch still returns the "Mechanical Keyboard" result because typo tolerance is enabled by default. No configuration required.
Filtering and sorting
Configure filterable and sortable attributes:
curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
-H 'Authorization: Bearer your-master-key' \
-H 'Content-Type: application/json' \
-d '{
"filterableAttributes": ["category", "price"],
"sortableAttributes": ["price"]
}'
Then search with filters:
curl 'http://localhost:7700/indexes/products/search' \
-H 'Authorization: Bearer your-search-key' \
-d '{
"q": "keyboard",
"filter": "category = Electronics AND price < 100",
"sort": ["price:asc"]
}'
Relevancy Tuning
Meilisearch's default ranking is good, but you can customize it:
curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
-H 'Authorization: Bearer your-master-key' \
-H 'Content-Type: application/json' \
-d '{
"searchableAttributes": ["name", "category", "description"],
"rankingRules": [
"words", "typo", "proximity", "attribute", "sort", "exactness"
]
}'
The searchableAttributes order matters — fields listed first are given higher weight. If "name" is listed before "description", matches in the product name rank higher than matches in the description.
Keeping Data in Sync
Meilisearch is a separate search index, not a database replacement. You need to sync data from your primary database. Common approaches:
- On write — Push updates to Meilisearch whenever your app writes to the database
- Periodic sync — Cron job that re-indexes changed records every few minutes
- Change data capture — Tools like Debezium that stream database changes to Meilisearch
For most self-hosted apps, the on-write approach is simplest:
// After saving to your database
await fetch('http://meilisearch:7700/indexes/products/documents', {
method: 'POST',
headers: {
'Authorization': 'Bearer your-admin-key',
'Content-Type': 'application/json'
},
body: JSON.stringify([updatedProduct])
});
Meilisearch handles upserts automatically — sending a document with an existing ID updates the record.
Meilisearch vs Elasticsearch vs Typesense
| Feature | Meilisearch | Elasticsearch | Typesense |
|---|---|---|---|
| Setup complexity | Very low | High | Low |
| RAM usage (small dataset) | ~100 MB | ~2-4 GB | ~100 MB |
| Typo tolerance | Built-in | Manual config | Built-in |
| Query speed | <50ms | <100ms | <50ms |
| Max dataset size | Millions | Billions | Millions |
| Configuration | Minimal | Extensive | Minimal |
| Geo search | Yes | Yes | Yes |
| Multi-tenancy | Built-in | Index-per-tenant | Built-in |
| License | MIT | SSPL / Elastic | GPL-3.0 |
When to choose Meilisearch
- You need user-facing search with instant results
- Your dataset is under 10 million documents
- You want the simplest possible setup
- You value typo tolerance out of the box
When to choose Elasticsearch
- You're doing log analytics or time-series search
- Your dataset is massive (billions of documents)
- You need complex aggregations and analytics
- You have the resources to manage a cluster
Production Hardening
Put it behind a reverse proxy
Don't expose Meilisearch directly to the internet. Use Caddy or Nginx:
search.yourdomain.com {
reverse_proxy meilisearch:7700
}
Use API keys properly
The master key generates two types of API keys:
# Get your API keys
curl 'http://localhost:7700/keys' \
-H 'Authorization: Bearer your-master-key'
- Search key — Safe to use in frontend JavaScript. Can only search, not modify data.
- Admin key — For backend indexing operations. Never expose this in client-side code.
Snapshots and backups
# Create a snapshot
curl -X POST 'http://localhost:7700/snapshots' \
-H 'Authorization: Bearer your-master-key'
Snapshots are stored in the data volume. Include this volume in your regular backup strategy.
The Bottom Line
Meilisearch does one thing exceptionally well: fast, typo-tolerant search for user-facing applications. If you're building an app with a search bar and your current solution is raw SQL queries, Meilisearch will transform the experience with minimal setup. Deploy it in Docker, push your data in via the REST API, and point your frontend at the search endpoint. You'll have better search than most SaaS products in under an hour.