Meilisearch Installation and Configuration
Meilisearch is an open-source, RESTful search engine written in Rust that delivers instant, typo-tolerant search results with a simple API and minimal configuration. This guide covers installing Meilisearch on Linux, creating indexes, managing documents, customizing search relevancy, filtering, API key management, and production deployment.
Prerequisites
- Ubuntu 20.04+ / Debian 11+ or CentOS 8+ / Rocky Linux 8+
- 512 MB RAM minimum (RAM improves performance)
- SSD storage for faster indexing
- Root or sudo access
Installing Meilisearch
# Quick install script (Linux x86_64)
curl -L https://install.meilisearch.com | sh
# Move to a system path
sudo mv meilisearch /usr/local/bin/
meilisearch --version
# Or download a specific release manually
MEILI_VERSION="v1.7.0"
wget https://github.com/meilisearch/meilisearch/releases/download/${MEILI_VERSION}/meilisearch-linux-amd64
chmod +x meilisearch-linux-amd64
sudo mv meilisearch-linux-amd64 /usr/local/bin/meilisearch
# Or use Docker
docker run -d \
--name meilisearch \
--restart unless-stopped \
-p 7700:7700 \
-e MEILI_MASTER_KEY='your-master-key-here' \
-v /opt/meilisearch/data:/meili_data \
getmeili/meilisearch:v1.7
Running Meilisearch as a Service
Create a systemd service for production:
# Create dedicated user and data directory
sudo useradd -r -s /sbin/nologin meilisearch
sudo mkdir -p /var/lib/meilisearch/data
sudo chown -R meilisearch:meilisearch /var/lib/meilisearch
# Generate a strong master key
openssl rand -base64 32
# Create environment config
sudo cat > /etc/meilisearch.env << 'EOF'
MEILI_DB_PATH=/var/lib/meilisearch/data
MEILI_HTTP_ADDR=127.0.0.1:7700
MEILI_MASTER_KEY=your-generated-master-key
MEILI_ENV=production
MEILI_LOG_LEVEL=INFO
EOF
sudo chmod 600 /etc/meilisearch.env
# Create systemd unit
sudo cat > /etc/systemd/system/meilisearch.service << 'EOF'
[Unit]
Description=Meilisearch Search Engine
After=network.target
[Service]
User=meilisearch
Group=meilisearch
EnvironmentFile=/etc/meilisearch.env
ExecStart=/usr/local/bin/meilisearch
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now meilisearch
sudo systemctl status meilisearch
# Test
curl http://localhost:7700/health
# Expected: {"status":"available"}
Set MEILI_HTTP_ADDR=0.0.0.0:7700 if you want direct access, or keep it on localhost and use Nginx as a reverse proxy (recommended).
Creating Indexes and Adding Documents
# Set your master key as a variable for convenience
MEILI_KEY="your-generated-master-key"
# Create an index (Meilisearch auto-creates on first document add)
curl -X POST http://localhost:7700/indexes \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{"uid": "movies", "primaryKey": "id"}'
# Add documents (array of JSON objects)
curl -X POST http://localhost:7700/indexes/movies/documents \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '[
{
"id": 1,
"title": "The Dark Knight",
"overview": "Batman raises the stakes in his war on crime with the help of Lt. Jim Gordon.",
"genres": ["Action", "Crime", "Drama"],
"release_year": 2008,
"rating": 9.0
},
{
"id": 2,
"title": "Inception",
"overview": "A thief who steals corporate secrets through dream-sharing technology.",
"genres": ["Action", "Sci-Fi", "Thriller"],
"release_year": 2010,
"rating": 8.8
},
{
"id": 3,
"title": "Interstellar",
"overview": "A team of explorers travel through a wormhole in space.",
"genres": ["Adventure", "Drama", "Sci-Fi"],
"release_year": 2014,
"rating": 8.6
}
]'
# The response returns a task UID - check its status
curl http://localhost:7700/tasks/0 \
-H "Authorization: Bearer ${MEILI_KEY}"
# Bulk import from NDJSON file
curl -X POST http://localhost:7700/indexes/movies/documents \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/x-ndjson' \
--data-binary @movies.ndjson
# Update documents (upsert by primary key)
curl -X PUT http://localhost:7700/indexes/movies/documents \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '[{"id": 1, "rating": 9.1}]'
# Delete a document
curl -X DELETE http://localhost:7700/indexes/movies/documents/1 \
-H "Authorization: Bearer ${MEILI_KEY}"
Searching
# Basic search (GET - simple)
curl "http://localhost:7700/indexes/movies/search?q=dark+knight" \
-H "Authorization: Bearer ${MEILI_KEY}"
# Advanced search (POST)
curl -X POST http://localhost:7700/indexes/movies/search \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"q": "space exploration",
"limit": 10,
"offset": 0,
"attributesToRetrieve": ["id", "title", "rating", "genres"],
"attributesToHighlight": ["title", "overview"],
"highlightPreTag": "<em>",
"highlightPostTag": "</em>",
"showMatchesPosition": true
}'
# Multi-search (batch multiple queries)
curl -X POST http://localhost:7700/multi-search \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"queries": [
{"indexUid": "movies", "q": "batman", "limit": 5},
{"indexUid": "movies", "q": "space", "limit": 5}
]
}'
Customizing Search Relevancy
Control how Meilisearch ranks results:
# Configure search settings
curl -X PATCH http://localhost:7700/indexes/movies/settings \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"searchableAttributes": [
"title",
"overview",
"genres"
],
"displayedAttributes": ["id", "title", "overview", "genres", "release_year", "rating"],
"rankingRules": [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"rating:desc"
],
"stopWords": ["the", "a", "an", "in", "on", "at"],
"synonyms": {
"sci-fi": ["science fiction"],
"scifi": ["science fiction"],
"sf": ["science fiction"]
},
"typoTolerance": {
"enabled": true,
"minWordSizeForTypos": {
"oneTypo": 5,
"twoTypos": 9
}
}
}'
searchableAttributes order matters: fields listed first are weighted higher. The rankingRules array determines tie-breaking order. Adding a custom attribute like rating:desc sorts by rating after standard relevance rules.
Filtering and Faceted Search
Enable filterable and sortable attributes:
# Update index settings to enable filtering
curl -X PATCH http://localhost:7700/indexes/movies/settings \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"filterableAttributes": ["genres", "release_year", "rating"],
"sortableAttributes": ["release_year", "rating"]
}'
# Wait for the settings task to complete before searching with filters
Search with filters:
curl -X POST http://localhost:7700/indexes/movies/search \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"q": "space",
"filter": "genres = \"Sci-Fi\" AND rating >= 8.5 AND release_year >= 2010",
"sort": ["rating:desc"],
"facets": ["genres", "release_year"]
}'
Filter syntax:
field = value- exact matchfield IN [val1, val2]- match anyfield >= valueANDfield <= value- rangeNOT field = value- negation- Combine with
AND/OR/ parentheses
The facets parameter returns distribution counts per value, useful for building filter UI.
API Keys and Multi-Tenancy
# Create a search-only API key (for frontend use)
curl -X POST http://localhost:7700/keys \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"name": "Frontend Search Key",
"description": "Read-only key for the frontend",
"actions": ["search"],
"indexes": ["movies"],
"expiresAt": "2025-12-31T00:00:00Z"
}'
# Create a key for a specific tenant using search rules
# This restricts searches to documents matching tenant_id
curl -X POST http://localhost:7700/keys \
-H "Authorization: Bearer ${MEILI_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"name": "Tenant A Key",
"actions": ["search"],
"indexes": ["*"],
"expiresAt": null
}'
For multi-tenant search with per-user filters, generate tenant tokens server-side using the SDK:
import meilisearch
client = meilisearch.Client('http://localhost:7700', 'your-master-key')
# Generate a tenant token with forced filter
# User can only see their own data
tenant_token = client.generate_tenant_token(
api_key_uid="your-search-key-uid",
search_rules={"movies": {"filter": "user_id = 42"}},
expires_at=datetime.now() + timedelta(hours=1)
)
Troubleshooting
Check index stats:
curl http://localhost:7700/indexes/movies/stats \
-H "Authorization: Bearer ${MEILI_KEY}"
# Shows number of documents, field distribution, is_indexing status
Task failed:
# List recent tasks
curl "http://localhost:7700/tasks?limit=20" \
-H "Authorization: Bearer ${MEILI_KEY}"
# Check specific task error
curl http://localhost:7700/tasks/5 \
-H "Authorization: Bearer ${MEILI_KEY}"
# Look at "error" field in response
Search returns no results:
- Confirm
searchableAttributesincludes the field you're matching against - Run the same query without filters to rule out filter issues
- Check that documents are fully indexed (task status =
succeeded)
Slow indexing:
# Check current tasks
curl "http://localhost:7700/tasks?statuses=processing,enqueued" \
-H "Authorization: Bearer ${MEILI_KEY}"
# Large document batches will take time - this is normal
Reset settings to defaults:
curl -X DELETE http://localhost:7700/indexes/movies/settings \
-H "Authorization: Bearer ${MEILI_KEY}"
Conclusion
Meilisearch provides developer-friendly instant search with typo tolerance, faceting, and filtering that's ready for production with minimal configuration. The straightforward REST API, built-in tenant tokens for multi-tenant applications, and sub-100ms search latency make it ideal for SaaS applications, e-commerce search, and documentation sites. Keep the master key secret, expose only search keys to the frontend, and place Meilisearch behind a reverse proxy with TLS for production deployments.


