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.

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 match
  • field IN [val1, val2] - match any
  • field >= value AND field <= value - range
  • NOT 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 searchableAttributes includes 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.