Meilisearch Installation: Fast Search Engine

Meilisearch is a lightning-fast search engine designed for excellent search experience with minimal configuration. It provides instant search suggestions, typo tolerance, filter facets, and multi-field search out of the box, all while remaining simple to deploy and operate. This guide covers installation, index management, document ingestion, API key security, Nginx integration, systemd service configuration, and advanced settings for building responsive search interfaces.

Table of Contents

Overview and Use Cases

Meilisearch excels at providing search experiences for e-commerce, documentation, knowledge bases, and any application where users need to find information quickly. Unlike traditional full-text search engines requiring complex configuration, Meilisearch provides sensible defaults while remaining highly customizable. Its REST API, multi-language support, and typo tolerance make it ideal for user-facing search interfaces where errors in user input should not prevent finding relevant results.

Meilisearch operates on the concept of indexes, each containing documents with searchable fields. Documents can contain nested objects and arrays, enabling complex data structures. Search results include relevance ranking, highlighting of matching terms, and facets for filtering results.

Installation

Install Meilisearch on Linux systems. Download the latest stable release:

# Download Meilisearch binary
curl -L https://github.com/meilisearch/meilisearch/releases/download/v1.6.0/meilisearch-linux-amd64 -o /tmp/meilisearch

# Move to system path
sudo mv /tmp/meilisearch /usr/local/bin/
sudo chmod +x /usr/local/bin/meilisearch

# Verify installation
meilisearch --version
meilisearch --help

Alternatively, use package managers:

# Ubuntu/Debian via APT
curl -L https://install.meilisearch.com | sh

# CentOS/RHEL via DNF (if available)
sudo dnf install meilisearch

# Docker installation
docker pull getmeili/meilisearch:latest
docker run -it --rm -p 7700:7700 getmeili/meilisearch:latest

Create a dedicated system user for Meilisearch:

# Create user
sudo useradd -r -s /bin/false meilisearch

# Create data directory
sudo mkdir -p /var/lib/meilisearch
sudo chown meilisearch:meilisearch /var/lib/meilisearch
sudo chmod 700 /var/lib/meilisearch

# Create logs directory
sudo mkdir -p /var/log/meilisearch
sudo chown meilisearch:meilisearch /var/log/meilisearch

Create a configuration file:

sudo nano /etc/meilisearch/meilisearch.conf

Add:

# Server settings
host = 0.0.0.0
port = 7700
socket = /var/run/meilisearch/meilisearch.sock

# Database location
db-path = /var/lib/meilisearch/data

# API key for securing requests
master-key = your-secure-master-key-here

# Maximum payload size
max-payload-size = 100

# Environment (test, development, production)
env = production

# Log level (error, warn, info, debug)
log-level = info

# Snapshots for backups
snapshots-dir = /var/lib/meilisearch/snapshots
dumps-dir = /var/lib/meilisearch/dumps

# SSL configuration
ssl-cert-path = /etc/meilisearch/certs/cert.pem
ssl-key-path = /etc/meilisearch/certs/key.pem

Service Configuration

Create a systemd service for Meilisearch:

sudo nano /etc/systemd/system/meilisearch.service

Add:

[Unit]
Description=Meilisearch Search Engine
Documentation=https://docs.meilisearch.com
After=network.target

[Service]
Type=simple
User=meilisearch
Group=meilisearch
WorkingDirectory=/var/lib/meilisearch

ExecStart=/usr/local/bin/meilisearch --db-path /var/lib/meilisearch/data \
  --master-key your-secure-master-key-here \
  --http-addr 127.0.0.1:7700

ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10

StandardOutput=journal
StandardError=journal

# Security settings
PrivateTmp=yes
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/meilisearch /var/log/meilisearch

[Install]
WantedBy=multi-user.target

Enable and start the service:

# Enable automatic startup
sudo systemctl daemon-reload
sudo systemctl enable meilisearch

# Start the service
sudo systemctl start meilisearch

# Check status
sudo systemctl status meilisearch

# Monitor logs
sudo journalctl -u meilisearch -f

Test the installation:

# Health check
curl http://localhost:7700/health

# Get version
curl http://localhost:7700/version

# List indexes
curl http://localhost:7700/indexes \
  -H "Authorization: Bearer your-api-key"

Index Management

Create and configure indexes for your search use case:

# Create index for products
curl -X POST http://localhost:7700/indexes \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "uid": "products",
    "primaryKey": "id"
  }'

# Create index for blog posts
curl -X POST http://localhost:7700/indexes \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "uid": "blog",
    "primaryKey": "id"
  }'

# Get index information
curl http://localhost:7700/indexes/products \
  -H "Authorization: Bearer your-api-key"

# Delete index
curl -X DELETE http://localhost:7700/indexes/products \
  -H "Authorization: Bearer your-api-key"

Configure index settings:

# Set searchable attributes
curl -X PUT http://localhost:7700/indexes/products/settings/searchable-attributes \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '["name", "description", "category", "tags"]'

# Set filterable attributes (for faceting)
curl -X PUT http://localhost:7700/indexes/products/settings/filterable-attributes \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '["category", "price", "in_stock", "brand"]'

# Set sortable attributes
curl -X PUT http://localhost:7700/indexes/products/settings/sortable-attributes \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '["price", "created_at", "rating"]'

# Configure ranking rules
curl -X PUT http://localhost:7700/indexes/products/settings/ranking-rules \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '[
    "words",
    "typo",
    "proximity",
    "exactness",
    "sort",
    "attribute",
    "price:asc",
    "rating:desc"
  ]'

# Set distinct attribute (for deduplication)
curl -X PUT http://localhost:7700/indexes/products/settings/distinct-attribute \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '"sku"'

# Configure synonyms
curl -X PUT http://localhost:7700/indexes/products/settings/synonyms \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": ["smartphone", "mobile", "iphone"],
    "laptop": ["notebook", "computer"],
    "shirt": ["t-shirt", "tee"]
  }'

Document Operations

Add and update documents in indexes:

# Add single document
curl -X POST http://localhost:7700/indexes/products/documents \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "1",
    "name": "Wireless Laptop Mouse",
    "description": "Ergonomic wireless mouse with USB receiver",
    "price": 29.99,
    "category": "Electronics",
    "tags": ["mouse", "wireless", "computer"],
    "in_stock": true,
    "rating": 4.5,
    "sku": "MOUSE-001"
  }'

# Add multiple documents
curl -X POST http://localhost:7700/indexes/products/documents \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": "2",
      "name": "Mechanical Keyboard",
      "description": "RGB mechanical keyboard with hot-swappable switches",
      "price": 149.99,
      "category": "Electronics",
      "tags": ["keyboard", "gaming", "mechanical"],
      "in_stock": true,
      "rating": 4.8,
      "sku": "KB-001"
    },
    {
      "id": "3",
      "name": "USB-C Hub",
      "description": "7-in-1 USB-C hub with HDMI, USB 3.0, and SD card reader",
      "price": 59.99,
      "category": "Electronics",
      "tags": ["usb", "hub", "connector"],
      "in_stock": true,
      "rating": 4.2,
      "sku": "HUB-001"
    }
  ]'

# Update document
curl -X PUT http://localhost:7700/indexes/products/documents/1 \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "1",
    "price": 24.99,
    "in_stock": false
  }'

# Delete document
curl -X DELETE http://localhost:7700/indexes/products/documents/1 \
  -H "Authorization: Bearer your-api-key"

# Get document
curl http://localhost:7700/indexes/products/documents/1 \
  -H "Authorization: Bearer your-api-key"

# List documents
curl "http://localhost:7700/indexes/products/documents?limit=100&offset=0" \
  -H "Authorization: Bearer your-api-key"

Batch import from JSON file:

# Create JSON file with documents
cat > /tmp/products.json << 'EOF'
[
  {"id": "1", "name": "Product 1", "price": 100},
  {"id": "2", "name": "Product 2", "price": 200}
]
EOF

# Upload via API
curl -X POST http://localhost:7700/indexes/products/documents \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d @/tmp/products.json

# Or use task endpoint for large datasets
curl -X POST http://localhost:7700/indexes/products/documents/import \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/x-ndjson" \
  --data-binary @/tmp/products.jsonl

Search and Filtering

Perform searches with various options:

# Basic search
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "wireless mouse"
  }'

# Search with filters
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "mouse",
    "filter": "price <= 50 AND in_stock = true",
    "limit": 20,
    "offset": 0
  }'

# Search with sorting
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "electronics",
    "sort": ["price:asc"],
    "limit": 10
  }'

# Search with highlighting
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "keyboard",
    "highlightPreTag": "<mark>",
    "highlightPostTag": "</mark>",
    "attributesToHighlight": ["name", "description"]
  }'

# Search with pagination
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "mouse",
    "limit": 20,
    "offset": 40
  }'

Implement typo tolerance:

# Configure typo tolerance
curl -X PUT http://localhost:7700/indexes/products/settings/typo-tolerance \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "minWordSizeForTypos": {
      "oneTypo": 5,
      "twoTypos": 9
    },
    "disableOnWords": ["id", "sku"],
    "disableOnAttributes": []
  }'

# Search with typos (finds results despite misspellings)
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"q": "keybord"}'  # Misspelled "keyboard"

Faceting and Aggregation

Use facets to enable filtering and exploration:

# Search with facets
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "electronics",
    "facets": ["category", "price", "rating"],
    "limit": 10
  }'

# Filter by facet value
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "mouse",
    "facets": ["category", "price"],
    "filter": "category = \"Electronics\" AND price >= 20 AND price <= 50"
  }'

# Facet stats for numeric fields
curl "http://localhost:7700/indexes/products/search" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "phone",
    "facets": ["price"],
    "limit": 5
  }'

API Keys and Security

Generate and manage API keys for different access levels:

# Create API key for search-only (public)
curl -X POST http://localhost:7700/api-keys \
  -H "Authorization: Bearer your-master-key" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Search only API key",
    "actions": ["search"],
    "indexes": ["products", "blog"],
    "expiresAt": "2025-12-31T23:59:59Z"
  }'

# Create API key for indexing (private)
curl -X POST http://localhost:7700/api-keys \
  -H "Authorization: Bearer your-master-key" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Indexing API key",
    "actions": ["documents.add", "documents.delete", "documents.update"],
    "indexes": ["products"],
    "expiresAt": "2025-12-31T23:59:59Z"
  }'

# List API keys
curl http://localhost:7700/api-keys \
  -H "Authorization: Bearer your-master-key"

# Delete API key
curl -X DELETE http://localhost:7700/api-keys/<key-id> \
  -H "Authorization: Bearer your-master-key"

Secure your Meilisearch instance:

# Update master key
meilisearch --master-key "new-very-secure-master-key-with-minimum-16-characters"

# Set environment to production
meilisearch --env production

# Use HTTPS
meilisearch --ssl-cert-path /etc/meilisearch/certs/cert.pem \
  --ssl-key-path /etc/meilisearch/certs/key.pem

# Disable HTTP (force HTTPS only)
# Configure via Nginx reverse proxy instead

Nginx Reverse Proxy

Configure Nginx as a reverse proxy with SSL/TLS:

sudo nano /etc/nginx/sites-available/meilisearch

Add:

upstream meilisearch {
  server 127.0.0.1:7700;
}

# Redirect HTTP to HTTPS
server {
  listen 80;
  listen [::]:80;
  server_name search.example.com;
  
  return 301 https://$server_name$request_uri;
}

# HTTPS server
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name search.example.com;
  
  # SSL certificates
  ssl_certificate /etc/letsencrypt/live/search.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/search.example.com/privkey.pem;
  
  # Security headers
  add_header X-Frame-Options "SAMEORIGIN" always;
  add_header X-XSS-Protection "1; mode=block" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header Referrer-Policy "no-referrer-when-downgrade" always;
  add_header Content-Security-Policy "default-src 'self'" always;
  
  # Proxy settings
  location / {
    proxy_pass http://meilisearch;
    proxy_http_version 1.1;
    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;
    proxy_buffering off;
    proxy_request_buffering off;
  }
  
  # Rate limiting
  limit_req_zone $binary_remote_addr zone=meilisearch_limit:10m rate=100r/s;
  location /indexes/*/search {
    limit_req zone=meilisearch_limit burst=200 nodelay;
    proxy_pass http://meilisearch;
  }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/meilisearch /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Advanced Settings

Configure advanced Meilisearch settings:

# Set pagination limits
curl -X PUT http://localhost:7700/indexes/products/settings/pagination \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "maxTotalHits": 10000
  }'

# Configure stopwords (words to ignore in search)
curl -X PUT http://localhost:7700/indexes/products/settings/stop-words \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '["the", "a", "an", "and", "or", "is", "in", "at"]'

# Create snapshots for backup
curl -X POST http://localhost:7700/snapshots \
  -H "Authorization: Bearer your-api-key"

# Get task status
curl "http://localhost:7700/tasks/<task-id>" \
  -H "Authorization: Bearer your-api-key"

# Create dump for export
curl -X POST http://localhost:7700/dumps \
  -H "Authorization: Bearer your-api-key"

Monitor Meilisearch performance:

# Check stats
curl http://localhost:7700/stats \
  -H "Authorization: Bearer your-api-key"

# View tasks (indexing operations)
curl "http://localhost:7700/tasks?limit=10&from=0" \
  -H "Authorization: Bearer your-api-key"

# Check index stats
curl http://localhost:7700/indexes/products/stats \
  -H "Authorization: Bearer your-api-key"

Conclusion

Meilisearch provides a modern, easy-to-deploy search solution with excellent defaults and powerful customization options. Its REST API, multi-language support, and built-in typo tolerance make it ideal for creating responsive user interfaces. By combining Meilisearch with Nginx for security and load balancing, you can build scalable search infrastructure that scales with your data. Whether building e-commerce product search, documentation search, or any full-text search interface, Meilisearch delivers fast, relevant results with minimal configuration complexity.