Meilisearch: Fast Search Engine
Typo-tolerant search engine. Easy to use with instant search results.
Introduction
Meilisearch is a powerful, open-source search engine that delivers instant search results with an out-of-the-box user experience. Built with Rust for blazing-fast performance, Meilisearch provides typo tolerance, filters, faceting, and synonyms support. It’s designed to be simple to deploy and integrate, making it perfect for adding search functionality to applications, documentation sites, e-commerce platforms, or content management systems.
By the end of this guide, you’ll have a production-ready Meilisearch installation that:
- Delivers sub-millisecond search results
- Handles typos and fuzzy matching automatically
- Provides a powerful REST API for integration
- Includes multi-index support for different data types
- Offers filtering, sorting, and faceting capabilities
- Features comprehensive security and authentication
Prerequisites
Before starting, ensure you have:
- A Linux server with at least 2GB RAM (4GB recommended for production)
- Docker and Docker Compose installed
- Basic understanding of REST APIs and JSON
- At least 10GB free disk space for indexes
- Root or sudo access
- Optional: Domain name for public access
What Makes Meilisearch Special?
Key Features
Instant Search: Results appear as users type, with response times under 50ms for most queries.
Typo Tolerance: Automatically handles misspellings and fuzzy matching without configuration.
Easy Integration: RESTful API with official SDKs for JavaScript, Python, Ruby, PHP, Go, Rust, and more.
Relevant Results: Built-in ranking rules that deliver the most relevant results first.
Faceted Search: Filter results by categories, tags, prices, dates, or any custom attributes.
Highlighting: Automatically highlights matching terms in search results.
Language Support: Works with any language, with special optimizations for Latin-based languages.
Use Cases
- Application Search: Add instant search to your web or mobile applications
- Documentation Sites: Make documentation searchable and discoverable
- E-commerce: Product search with filters, sorting, and faceting
- Content Management: Search articles, blog posts, and media
- Site Search: Replace slow SQL queries with fast full-text search
Architecture Overview
Our setup consists of:
- Meilisearch Server: Main search engine with HTTP API
- Persistent Volume: Stores indexes and data
- API Keys: Master key and search-only keys for security
- Optional Reverse Proxy: Nginx or Traefik for HTTPS access
The search flow:
- Documents are indexed via the REST API
- Meilisearch builds searchable indexes in memory
- Search queries hit the /search endpoint
- Results are returned with highlighting and relevance ranking
- Filters and facets can be applied in real-time
Directory Structure Setup
Create the necessary directory structure:
mkdir -p ~/meilisearch/{data,config,scripts}
cd ~/meilisearch Docker Compose Configuration
Create a docker-compose.yml file:
version: '3.8'
services:
meilisearch:
container_name: meilisearch
image: getmeili/meilisearch:v1.6
restart: unless-stopped
ports:
- "7700:7700"
environment:
# CRITICAL: Change this to a strong random string
- MEILI_MASTER_KEY=YOUR_SUPER_SECRET_MASTER_KEY_MINIMUM_16_CHARS
- MEILI_ENV=production
- MEILI_HTTP_ADDR=0.0.0.0:7700
- MEILI_DB_PATH=/meili_data
- MEILI_LOG_LEVEL=INFO
# Optional: Enable analytics (set to false for privacy)
- MEILI_NO_ANALYTICS=true
# Optional: Set max payload size (in bytes)
- MEILI_HTTP_PAYLOAD_SIZE_LIMIT=104857600
volumes:
- ./data:/meili_data
networks:
- meilisearch_net
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--spider", "http://localhost:7700/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
networks:
meilisearch_net:
driver: bridge Important Notes:
- Replace
YOUR_SUPER_SECRET_MASTER_KEY_MINIMUM_16_CHARSwith a strong random key - Generate a secure key with:
openssl rand -base64 32 - The master key is required in production mode and must be at least 16 characters
- Keep your master key secret and never commit it to version control
Generating a Secure Master Key
Generate a strong master key:
# Generate a 32-character random key
openssl rand -base64 32
# Or use this alternative
head -c 32 /dev/urandom | base64 Update your docker-compose.yml with the generated key.
Starting Meilisearch
Start the container:
docker compose up -d Verify it’s running:
docker compose ps
docker compose logs -f meilisearch Check the health endpoint:
curl http://localhost:7700/health You should see: {"status":"available"}
API Authentication
Meilisearch uses API keys for authentication. There are two types:
1. Master Key
The master key has full access to all operations. Use it for:
- Creating and managing indexes
- Adding and updating documents
- Managing API keys
- Administrative tasks
2. API Keys
Generate search-only and admin API keys:
# Get the default admin API key (for adding documents)
curl -X GET 'http://localhost:7700/keys' -H 'Authorization: Bearer YOUR_MASTER_KEY'
# Create a custom search-only API key
curl -X POST 'http://localhost:7700/keys' -H 'Authorization: Bearer YOUR_MASTER_KEY' -H 'Content-Type: application/json' --data-binary '{
"description": "Search-only key for frontend",
"actions": ["search"],
"indexes": ["*"],
"expiresAt": null
}' Save the generated keys securely. Use the search-only key in your frontend applications.
Creating Your First Index
Create an index for a blog or product catalog:
# Create an index
curl -X POST 'http://localhost:7700/indexes' -H 'Authorization: Bearer YOUR_MASTER_KEY' -H 'Content-Type: application/json' --data-binary '{
"uid": "articles",
"primaryKey": "id"
}' Configuring Index Settings
Configure searchable attributes, ranking rules, and filters:
# Update index settings
curl -X PATCH 'http://localhost:7700/indexes/articles/settings' -H 'Authorization: Bearer YOUR_MASTER_KEY' -H 'Content-Type: application/json' --data-binary '{
"searchableAttributes": [
"title",
"content",
"tags"
],
"filterableAttributes": [
"category",
"author",
"published_date",
"tags"
],
"sortableAttributes": [
"published_date",
"title"
],
"rankingRules": [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness"
],
"stopWords": [
"the",
"a",
"an"
],
"synonyms": {
"docker": ["container", "containerization"],
"kubernetes": ["k8s", "k3s"]
},
"distinctAttribute": null,
"typoTolerance": {
"enabled": true,
"minWordSizeForTypos": {
"oneTypo": 5,
"twoTypos": 9
}
}
}' Adding Documents
Add documents to your index:
# Add documents in bulk
curl -X POST 'http://localhost:7700/indexes/articles/documents' -H 'Authorization: Bearer YOUR_MASTER_KEY' -H 'Content-Type: application/json' --data-binary '[
{
"id": 1,
"title": "Getting Started with Docker",
"content": "Docker is a platform for developing, shipping, and running applications in containers...",
"category": "DevOps",
"author": "Homelab Team",
"tags": ["docker", "containers", "devops"],
"published_date": 1705276800
},
{
"id": 2,
"title": "Kubernetes vs Docker Swarm",
"content": "Comparing container orchestration platforms...",
"category": "DevOps",
"author": "Homelab Team",
"tags": ["kubernetes", "docker", "orchestration"],
"published_date": 1705363200
}
]' Performing Searches
Search your indexed documents:
# Basic search
curl -X POST 'http://localhost:7700/indexes/articles/search' -H 'Authorization: Bearer YOUR_SEARCH_KEY' -H 'Content-Type: application/json' --data-binary '{
"q": "docker container"
}'
# Advanced search with filters and sorting
curl -X POST 'http://localhost:7700/indexes/articles/search' -H 'Authorization: Bearer YOUR_SEARCH_KEY' -H 'Content-Type: application/json' --data-binary '{
"q": "docker",
"filter": "category = DevOps AND published_date > 1705276800",
"sort": ["published_date:desc"],
"attributesToHighlight": ["title", "content"],
"highlightPreTag": "<mark>",
"highlightPostTag": "</mark>",
"limit": 20,
"offset": 0
}'
# Faceted search
curl -X POST 'http://localhost:7700/indexes/articles/search' -H 'Authorization: Bearer YOUR_SEARCH_KEY' -H 'Content-Type: application/json' --data-binary '{
"q": "homelab",
"facets": ["category", "author", "tags"]
}' JavaScript Integration Example
Here’s how to integrate Meilisearch in a web application:
// Install: npm install meilisearch
import { MeiliSearch } from 'meilisearch';
const client = new MeiliSearch({
host: 'http://localhost:7700',
apiKey: 'YOUR_SEARCH_KEY'
});
// Perform a search
async function searchArticles(query) {
const index = client.index('articles');
const results = await index.search(query, {
filter: 'category = DevOps',
attributesToHighlight: ['title', 'content'],
limit: 10
});
return results;
}
// Add documents
async function addArticle(article) {
const index = client.index('articles');
await index.addDocuments([article]);
}
// Update documents
async function updateArticle(id, updates) {
const index = client.index('articles');
await index.updateDocuments([{ id, ...updates }]);
}
// Delete documents
async function deleteArticle(id) {
const index = client.index('articles');
await index.deleteDocument(id);
} Python Integration Example
Using Meilisearch with Python:
# Install: pip install meilisearch
import meilisearch
client = meilisearch.Client('http://localhost:7700', 'YOUR_SEARCH_KEY')
# Get index
index = client.index('articles')
# Search
results = index.search('docker', {
'filter': 'category = DevOps',
'attributesToHighlight': ['title', 'content'],
'limit': 10
})
# Add documents
articles = [
{
'id': 3,
'title': 'Homelab Setup Guide',
'content': 'Building your first homelab...',
'category': 'Homelab',
'author': 'Homelab Team',
'tags': ['homelab', 'hardware'],
'published_date': 1705449600
}
]
index.add_documents(articles)
# Update documents
index.update_documents([
{'id': 3, 'title': 'Updated Homelab Setup Guide'}
])
# Delete documents
index.delete_document(3) Instant Search UI
Create a simple instant search interface:
<!DOCTYPE html>
<html>
<head>
<title>Instant Search</title>
<script src="https://cdn.jsdelivr.net/npm/meilisearch@latest/dist/bundles/meilisearch.umd.js"></script>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; }
#search-box { width: 100%; padding: 12px; font-size: 16px; border: 2px solid #ddd; }
.result { padding: 15px; border-bottom: 1px solid #eee; }
.result h3 { margin: 0 0 10px 0; }
mark { background-color: yellow; font-weight: bold; }
</style>
</head>
<body>
<h1>Search Articles</h1>
<input type="text" id="search-box" placeholder="Search...">
<div id="results"></div>
<script>
const client = new MeiliSearch({
host: 'http://localhost:7700',
apiKey: 'YOUR_SEARCH_KEY'
});
const searchBox = document.getElementById('search-box');
const resultsDiv = document.getElementById('results');
searchBox.addEventListener('input', async (e) => {
const query = e.target.value;
if (query.length < 2) {
resultsDiv.innerHTML = '';
return;
}
const index = client.index('articles');
const results = await index.search(query, {
attributesToHighlight: ['title', 'content'],
highlightPreTag: '<mark>',
highlightPostTag: '</mark>',
limit: 10
});
displayResults(results.hits);
});
function displayResults(hits) {
if (hits.length === 0) {
resultsDiv.innerHTML = '<p>No results found</p>';
return;
}
resultsDiv.innerHTML = hits.map(hit => `
<div class="result">
<h3>${hit._formatted.title}</h3>
<p>${hit._formatted.content.substring(0, 200)}...</p>
<small>Category: ${hit.category} | Author: ${hit.author}</small>
</div>
`).join('');
}
</script>
</body>
</html> Backup and Restore
Create a backup script:
#!/bin/bash
# ~/meilisearch/scripts/backup.sh
BACKUP_DIR=~/meilisearch/backups
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/meilisearch_backup_$TIMESTAMP.tar.gz"
echo "Creating backup..."
# Stop Meilisearch
cd ~/meilisearch
docker compose stop meilisearch
# Create backup
tar -czf "$BACKUP_FILE" data/
# Start Meilisearch
docker compose start meilisearch
# Keep only last 7 backups
ls -t "$BACKUP_DIR"/meilisearch_backup_*.tar.gz | tail -n +8 | xargs -r rm
echo "Backup completed: $BACKUP_FILE" Make it executable and schedule:
chmod +x ~/meilisearch/scripts/backup.sh
# Add to crontab for daily backups at 2 AM
crontab -e
# Add: 0 2 * * * /home/YOUR_USER/meilisearch/scripts/backup.sh Monitoring and Stats
Get index statistics:
# Get index stats
curl -X GET 'http://localhost:7700/indexes/articles/stats' -H 'Authorization: Bearer YOUR_MASTER_KEY'
# Get all indexes
curl -X GET 'http://localhost:7700/indexes' -H 'Authorization: Bearer YOUR_MASTER_KEY'
# Get system stats
curl -X GET 'http://localhost:7700/stats' -H 'Authorization: Bearer YOUR_MASTER_KEY'
# Check version
curl -X GET 'http://localhost:7700/version' -H 'Authorization: Bearer YOUR_MASTER_KEY' Reverse Proxy with Nginx
Secure Meilisearch with HTTPS:
server {
listen 80;
server_name search.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name search.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/search.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/search.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:7700;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS headers for search API
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Authorization, Content-Type';
if ($request_method = 'OPTIONS') {
return 204;
}
}
} Performance Tuning
Optimize for production:
# docker-compose.yml additions
services:
meilisearch:
# ... existing config ...
environment:
# Increase max indexing threads
- MEILI_MAX_INDEXING_THREADS=4
# Increase max indexing memory (in bytes, 2GB)
- MEILI_MAX_INDEXING_MEMORY=2147483648
# Set max task database size (in bytes, 1GB)
- MEILI_MAX_TASK_DB_SIZE=1073741824
# Enable gzip compression
- MEILI_EXPERIMENTAL_ENABLE_METRICS=true
deploy:
resources:
limits:
memory: 4G
reservations:
memory: 2G Troubleshooting
High Memory Usage
# Check memory usage
docker stats meilisearch
# Reduce max indexing memory
# Update docker-compose.yml:
- MEILI_MAX_INDEXING_MEMORY=1073741824 # 1GB Slow Indexing
# Increase indexing threads
- MEILI_MAX_INDEXING_THREADS=8
# Check task queue
curl -X GET 'http://localhost:7700/tasks' -H 'Authorization: Bearer YOUR_MASTER_KEY' Search Not Returning Results
# Check if documents are indexed
curl -X GET 'http://localhost:7700/indexes/articles/stats' -H 'Authorization: Bearer YOUR_MASTER_KEY'
# Verify searchable attributes
curl -X GET 'http://localhost:7700/indexes/articles/settings/searchable-attributes' -H 'Authorization: Bearer YOUR_MASTER_KEY' CORS Issues
Add CORS headers in reverse proxy or use Meilisearch’s built-in CORS:
environment:
- MEILI_HTTP_CORS_ALLOW_ORIGIN=* Container Won’t Start
# Check logs
docker compose logs meilisearch
# Verify master key length
echo -n "YOUR_MASTER_KEY" | wc -c
# Must be at least 16 characters
# Check port conflicts
sudo netstat -tulpn | grep 7700 Security Best Practices
- Use Strong Master Keys: Generate cryptographically secure random keys
- Limit API Key Permissions: Create separate keys for search and admin operations
- Use HTTPS: Always use SSL/TLS in production
- Restrict Network Access: Use firewall rules to limit access
- Regular Updates: Keep Meilisearch updated to the latest version
- Monitor Logs: Enable logging and monitor for suspicious activity
- Backup Regularly: Automate backups of your data directory
Conclusion
You now have a production-ready Meilisearch installation providing lightning-fast search capabilities. Meilisearch’s instant search, typo tolerance, and easy integration make it perfect for adding search to any application.
For more advanced features, explore:
- Multi-tenancy with index isolation
- Geo-search capabilities
- Custom ranking formulas
- Search analytics and monitoring
- Meilisearch Cloud for managed hosting
Happy searching!