Meilisearch is an open-source search engine built for instant, typo-tolerant, “search as you type” experiences. It’s far simpler to run than Elasticsearch: a single binary, sensible defaults, and great relevance out of the box. Use it for product search in a shop, documentation search, or any app where LIKE '%term%' in SQL no longer cuts it.
When Meilisearch fits
- Site and app search with typo tolerance, prefix search, facets and filters.
- Datasets from thousands to tens of millions of documents.
- Teams that want good search without tuning analyzers and mappings.
For log analytics, complex aggregations or very large clusters, Elasticsearch or OpenSearch are better fits. See Elasticsearch vs Meilisearch.
Install with Docker (recommended)
MEILI_KEY=$(openssl rand -base64 32)
echo "MEILI_MASTER_KEY=$MEILI_KEY" | sudo tee /etc/meili.env # keep it safe
sudo chmod 600 /etc/meili.env
docker run -d --name meilisearch --restart unless-stopped \
-p 127.0.0.1:7700:7700 \
--env-file /etc/meili.env \
-e MEILI_ENV=production \
-v meili-data:/meili_data \
getmeili/meilisearch:v1.12
(Use the current version tag.)
MEILI_ENV=productionrequires a master key and disables the built-in preview UI.- Bind to localhost, and expose it through a reverse proxy with HTTPS (below).
Or the binary with systemd
curl -L https://install.meilisearch.com | sh
sudo mv ./meilisearch /usr/local/bin/
Run it as a dedicated user with --master-key, --env production and --db-path /var/lib/meilisearch/data.ms under a systemd unit. See systemd unit files.
Add documents
Meilisearch takes JSON documents with a primary key:
curl -X POST 'http://127.0.0.1:7700/indexes/products/documents?primaryKey=id' \
-H "Authorization: Bearer $MEILI_KEY" -H 'Content-Type: application/json' \
--data-binary @products.json
products.json:
[
{ "id": 1, "name": "Wireless Keyboard", "brand": "Acme", "price": 49, "category": "accessories" },
{ "id": 2, "name": "Mechanical Keyboard", "brand": "KeyCo", "price": 129, "category": "accessories" }
]
Indexing is asynchronous. Check task status with GET /tasks.
Search
curl 'http://127.0.0.1:7700/indexes/products/search' \
-H "Authorization: Bearer $MEILI_KEY" -H 'Content-Type: application/json' \
--data '{ "q": "keybaord", "limit": 10 }'
The typo “keybaord” still finds keyboards.
Configure relevance and filters
curl -X PATCH 'http://127.0.0.1:7700/indexes/products/settings' \
-H "Authorization: Bearer $MEILI_KEY" -H 'Content-Type: application/json' \
--data '{
"searchableAttributes": ["name", "brand", "description"],
"filterableAttributes": ["category", "brand", "price"],
"sortableAttributes": ["price"],
"displayedAttributes": ["id", "name", "brand", "price"]
}'
Then filter and facet:
{ "q": "keyboard", "filter": "category = accessories AND price < 100", "facets": ["brand"], "sort": ["price:asc"] }
- searchableAttributes order matters: earlier fields weigh more.
- Synonyms, stop words, ranking rules and typo tolerance are all adjustable in settings.
API keys: never expose the master key
The master key is for administration only. Create restricted keys:
- a search-only key for the frontend (browser JavaScript), limited to specific indexes;
- an admin key for your backend to add or update documents.
curl -X POST 'http://127.0.0.1:7700/keys' \
-H "Authorization: Bearer $MEILI_KEY" -H 'Content-Type: application/json' \
--data '{ "description": "frontend search", "actions": ["search"], "indexes": ["products"], "expiresAt": null }'
For per-user restrictions (multi-tenant data), use tenant tokens generated by your backend.
HTTPS with Nginx
server {
server_name search.example.com;
location / {
proxy_pass http://127.0.0.1:7700;
proxy_set_header Host $host;
client_max_body_size 100m; # for document uploads
}
# listen 443 ssl + certbot
}
See Let’s Encrypt for Nginx. Add CORS settings if the browser calls it directly from another domain.
Keep data in sync
- Push changes from your app (on create, update and delete) through the API. Official SDKs exist for JS, PHP, Python, Ruby, Go and more.
- Integrations: Laravel Scout, Strapi, WordPress plugins, and Firebase and other connectors.
- Batch-reindex nightly as a safety net.
Resources and backups
- RAM: Meilisearch uses memory-mapped files. Give it RAM comparable to the index size for best speed. Indexing large datasets is CPU-intensive.
- Disk: the index is larger than the raw JSON. Plan 2–5× the raw size.
- Snapshots/dumps:
POST /dumpscreates a portable dump (used for upgrades). Scheduled snapshots are available via launch options. Copy them off the server. See backup strategy. - Upgrades between major versions may need a dump and re-import. Read the release notes.
Quick reference
| Task | Endpoint |
|---|---|
| Add documents | POST /indexes/{uid}/documents |
| Search | POST /indexes/{uid}/search |
| Settings | PATCH /indexes/{uid}/settings |
| Task status | GET /tasks |
| Create key | POST /keys |
| Dump | POST /dumps |
| Health | GET /health |
Related: Typesense and MySQL EXPLAIN.