Skip to main content
โšก Calmops

Meilisearch Operations: Deployment, Scaling, and Monitoring

Introduction

Running Meilisearch in production requires careful attention to deployment, configuration, security, and maintenance. While Meilisearch is designed to be simple to set up, production environments demand a different approach to ensure reliability, performance, and security.

This guide covers the operational aspects of running Meilisearch at scale. We will explore deployment strategies, configuration options, security best practices, monitoring approaches, backup strategies, and performance optimization techniques. By the end, you will be equipped to run Meilisearch confidently in production environments.

Deployment Strategies

Choosing the right deployment strategy depends on your requirements for availability, scale, and operational complexity.

Single Node Deployment

For many applications, a single Meilisearch instance provides sufficient performance. This approach is simple to set up and maintain while offering excellent search speed for most use cases.

docker run -d \
  --name meilisearch \
  -p 7700:7700 \
  -v meili_data:/meili_data \
  -e MEILI_MASTER_KEY=your_master_key \
  -e MEILI_DB_PATH=/meili_data \
  -e MEILI_ENV=production \
  getmeilisearch/meilisearch:v1.12

A single node can handle millions of documents and thousands of queries per second. If your requirements exceed these thresholds, consider scaling horizontally.

Container Orchestration

For production deployments, container orchestration platforms provide essential features like automatic restarts, rolling updates, and scaling capabilities.

Docker Compose

For simpler production setups, Docker Compose provides a good balance of features:

version: '3.8'
services:
  meilisearch:
    image: getmeilisearch/meilisearch:v1.12
    ports:
      - "7700:7700"
    volumes:
      - meili_data:/meili_data
    environment:
      - MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
      - MEILI_DB_PATH=/meili_data
      - MEILI_ENV=production
      - MEILI_LOG_LEVEL=info
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:7700/health"]
      interval: 30s
      timeout: 10s
      retries: 3

Kubernetes Deployment

For larger deployments, Kubernetes provides advanced orchestration capabilities:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: meilisearch
spec:
  replicas: 1
  selector:
    matchLabels:
      app: meilisearch
  template:
    metadata:
      labels:
        app: meilisearch
    spec:
      containers:
      - name: meilisearch
        image: getmeilisearch/meilisearch:v1.12
        ports:
        - containerPort: 7700
        env:
        - name: MEILI_MASTER_KEY
          valueFrom:
            secretKeyRef:
              name: meilisearch-secrets
              key: master-key
        - name: MEILI_ENV
          value: "production"
        volumeMounts:
        - name: meili-data
          mountPath: /meili_data
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 7700
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 7700
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: meili-data
        persistentVolumeClaim:
          claimName: meilisearch-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: meilisearch
spec:
  selector:
    app: meilisearch
  ports:
  - port: 7700
    targetPort: 7700
  type: ClusterIP

High Availability Setup

For production systems requiring high availability, consider deploying multiple Meilisearch instances behind a load balancer. While Meilisearch does not have built-in clustering, you can achieve high availability through:

  1. Multiple Meilisearch instances sharing the same data storage
  2. A load balancer distributing requests
  3. A replicated database backing store
# docker-compose.yml for HA setup
version: '3.8'
services:
  meilisearch-1:
    image: getmeilisearch/meilisearch:v1.12
    volumes:
      - meili_data:/meili_data
    environment:
      - MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
      - MEILI_DB_PATH=/meili_data
      - MEILI_ENV=production
    depends_on:
      - nfs-server

  meilisearch-2:
    image: getmeilisearch/meilisearch:v1.12
    volumes:
      - meili_data:/meili_data
    environment:
      - MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
      - MEILI_DB_PATH=/meili_data
      - MEILI_ENV=production
    depends_on:
      - nfs-server

  nfs-server:
    image: itsthenetwork/nfs-server-alpine:latest
    volumes:
      - ./nfs:/exports
    environment:
      - SHARED_DIRECTORY=/exports

  load-balancer:
    image: nginx:alpine
    ports:
      - "7700:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - meilisearch-1
      - meilisearch-2

Configuration

Meilisearch provides numerous configuration options to optimize performance and behavior.

Environment Variables

Key environment variables for production deployments:

# Required
MEILI_MASTER_KEY=your_secure_master_key

# Paths and storage
MEILI_DB_PATH=/var/lib/meilisearch
MEILI_SNAPSHOT_DIR=/var/lib/meilisearch/snapshots

# HTTP server
MEILI_HOST=0.0.0.0
MEILI_PORT=7700

# Environment
MEILI_ENV=production
MEILI_LOG_LEVEL=info

# Performance
MEILI_MAX_INDEXING_THREADS=4
MEILI_MAX_SEARCH_THREADS=4

# Features
MEILI_EXPERIMENTAL_VECTOR_STORE=true

Configuration File

For complex configurations, use a configuration file:

# config.toml
master-key = "your_secure_master_key"

[server]
host = "0.0.0.0"
port = 7700

[db]
path = "/var/lib/meilisearch"

[logging]
level = "info"
format = "json"

[indexing]
max_indexing_memory = "1GiB"
max_indexing_threads = 4

[experimental]
vector_store = true

SSL/TLS Configuration

In production, always use SSL/TLS to encrypt communications:

# Generate self-signed certificates for testing
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout.key -out.crt \
  -subj "/C=US/ST=State/L=City/O=Organization"

# Start Meilisearch with SSL
./meilisearch --ssl-cert-path=./server.crt \
  --ssl-key-path=./server.key \
  --master-key="your_master_key"

For production, use certificates from a trusted Certificate Authority.

Security

Securing your Meilisearch deployment protects sensitive data and prevents unauthorized access.

API Keys

Meilisearch supports granular API key permissions:

# Create an API key with search-only permission
curl -X POST 'http://localhost:7700/keys' \
  -H 'Authorization: Bearer your_master_key' \
  -H 'Content-Type: application/json' \
  -d '{
    "description": "Search-only key for frontend",
    "actions": ["search"],
    "indexes": ["products", "documents"],
    "expiresAt": "2027-01-01T00:00:00Z"
  }'

Key actions include:

  • search - Execute search queries
  • documents.add - Add documents
  • documents.delete - Delete documents
  • indexes.create - Create new indexes
  • settings.update - Update index settings
  • stats.all - Access statistics
  • version - View version information

Key Expiration

Always set expiration dates for API keys:

curl -X POST 'http://localhost:7700/keys' \
  -H 'Authorization: Bearer your_master_key' \
  -H 'Content-Type: application/json' \
  -d '{
    "description": "Temporary development key",
    "actions": ["search"],
    "indexes": ["*"],
    "expiresAt": "2026-04-01T00:00:00Z"
  }'

Tenant Tokens

For multi-tenant applications, use tenant tokens to provide per-customer access:

const { MeiliSearch } = require('meilisearch')

// Generate a tenant token (server-side)
const client = new MeiliSearch({
  host: 'http://localhost:7700',
  apiKey: 'your_master_key'
})

const tenantToken = client.generateTenantToken(
  'tenant-id-123',
  {
    search: ['allowed-index-1', 'allowed-index-2']
  },
  { expiresIn: '1h' }
)

// The frontend uses this token
const frontendClient = new Meilisearch({
  host: 'http://localhost:7700',
  apiKey: tenantToken
})

Network Security

Restrict network access to your Meilisearch instance:

# Using firewall (UFW)
ufw allow 7700/tcp from 10.0.0.0/8
ufw enable

# Using Meilisearch's built-in options
MEILI_TRUSTED_IP_FORWARDING=10.0.0.0/8

HTTPS Enforcement

Always use HTTPS in production. Configure your reverse proxy to enforce SSL:

server {
    listen 443 ssl http2;
    server_name search.example.com;

    ssl_certificate /etc/ssl/certs/search.crt;
    ssl_certificate_key /etc/ssl/private/search.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    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;
    }
}

server {
    listen 80;
    server_name search.example.com;
    return 301 https://$server_name$request_uri;
}

Monitoring

Effective monitoring helps you understand system health and identify issues before they impact users.

Health Check

Meilisearch provides a health endpoint:

curl http://localhost:7700/health

Response:

{
  "status": "available",
  "indexes": 3,
  "version": "1.12.0"
}

Statistics Endpoint

Get detailed statistics about your indexes:

curl -X GET 'http://localhost:7700/indexes/books/stats' \
  -H 'Authorization: Bearer your_master_key'

Response:

{
  "numberOfDocuments": 100000,
  "isIndexing": false,
  "fieldDistribution": {
    "title": 100000,
    "author": 100000,
    "description": 100000
  }
}

Prometheus Metrics

Configure Prometheus to scrape Meilisearch metrics:

# prometheus.yml
scrape_configs:
  - job_name: 'meilisearch'
    static_configs:
      - targets: ['meilisearch:7700']

Add metrics configuration to Meilisearch:

MEILI_EXPERIMENTAL_METRICS_ENABLED=true

Access metrics at /metrics:

curl http://localhost:7700/metrics

Logging

Configure logging for troubleshooting:

# Set log level
MEILI_LOG_LEVEL=debug  # debug, info, warn, error

# Log to file
MEILI_LOG_FILE=/var/log/meilisearch.log

Structured Monitoring Stack

For comprehensive monitoring, integrate with established tools:

version: '3.8'
services:
  meilisearch:
    image: getmeilisearch/meilisearch:v1.12
    # ... configuration

  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

Backup and Restore

Protecting your data requires regular backups and a tested restore process.

Snapshots

Meilisearch supports creating snapshots:

# Create a snapshot manually
curl -X POST 'http://localhost:7700/snapshots' \
  -H 'Authorization: Bearer your_master_key'

Configure automatic snapshots:

MEILI_SNAPSHOT_DIR=/var/lib/meilisearch/snapshots
MEILI_SCHEDULE_SNAPSHOT=true
MEILI_SNAPSHOT_INTERVAL_SEC=3600  # Every hour

Backup Process

For complete backups, create snapshots and verify them:

#!/bin/bash
set -e

MEILI_HOST="http://localhost:7700"
MEILI_KEY="your_master_key"
BACKUP_DIR="/backups/meilisearch"
DATE=$(date +%Y%m%d_%H%M%S)

# Create snapshot
echo "Creating snapshot..."
curl -X POST "${MEILI_HOST}/snapshots" \
  -H "Authorization: Bearer ${MEILI_KEY}"

# Wait for snapshot to complete
sleep 10

# Copy snapshot to backup location
SNAPSHOT_PATH=$(curl -s -X GET "${MEILI_HOST}/snapshots" \
  -H "Authorization: Bearer ${MEILI_KEY}" | jq -r '.snapshots[0].file')

cp "${MEILI_SNAPSHOT_DIR}/${SNAPSHOT_PATH}" "${BACKUP_DIR}/meilisearch_${DATE}.snap"

# Verify backup
ls -lh "${BACKUP_DIR}/meilisearch_${DATE}.snap"

echo "Backup completed: ${BACKUP_DIR}/meilisearch_${DATE}.snap"

Restore from Snapshot

To restore from a snapshot:

# Stop Meilisearch
docker stop meilisearch

# Restore snapshot directory
docker run -v meili_data:/meili_data \
  -v $(pwd)/snapshots:/snapshots:ro \
  getmeilisearch/meilisearch:v1.12 \
  ./meilisearch --import-snapshot /snapshots/your_snapshot.snap

# Restart Meilisearch
docker start meilisearch

Export/Import

For smaller datasets, export documents as JSON:

# Export all documents from an index
curl -X GET 'http://localhost:7700/indexes/books/documents?limit=100000' \
  -H 'Authorization: Bearer your_master_key' \
  -o backup_documents.json

Performance Optimization

Optimizing Meilisearch ensures fast response times even under heavy load.

Resource Allocation

Allocate appropriate resources based on your workload:

# Recommended for medium workloads
environment:
  - MEILI_MAX_INDEXING_THREADS=4
  - MEILI_MAX_SEARCH_THREADS=4
  - MEILI_MAX_INDEXING_MEMORY=2GiB
resources:
  requests:
    memory: "1Gi"
    cpu: "500m"
  limits:
    memory: "4Gi"
    cpu: "2000m"

Indexing Optimization

Speed up indexing with proper configuration:

# Batch document additions for better performance
# documents in batches of Add 1000 instead of individually

# Configure indexing threads based on CPU cores
MEILI_MAX_INDEXING_THREADS=8  # For 8+ core systems

Search Optimization

Optimize search performance:

# Limit the number of hits returned when not needed
# Use 'limit' parameter in search requests

# Configure searchable attributes to exclude large fields
curl -X PATCH 'http://localhost:7700/indexes/books/settings' \
  -H 'Authorization: Bearer your_master_key' \
  -H 'Content-Type: application/json' \
  -d '{
    "searchableAttributes": ["title", "author", "genre"],
    "displayedAttributes": ["*"]
  }'

Caching

Implement caching at the application or proxy level:

# Nginx caching configuration
proxy_cache_path /tmp/meilisearch_cache levels=1:2 
  keys_zone=meilisearch_cache:10m 
  max_size=100m 
  inactive=60m;

server {
    location / {
        proxy_pass http://localhost:7700;
        proxy_cache meilisearch_cache;
        proxy_cache_valid 200 60m;
        proxy_cache_key "$request_uri";
        add_header X-Cache-Status $upstream_cache_status;
    }
}

Query Optimization

Structure queries efficiently:

// Bad: Fetching more data than needed
const results = await index.search('query', {
  limit: 1000,  // Too high
  attributesToRetrieve: ['*']  // Retrieve all fields
})

// Good: Fetch only needed data
const results = await index.search('query', {
  limit: 20,  // Appropriate limit
  attributesToRetrieve: ['id', 'title', 'price'],  // Only needed fields
  attributesToHighlight: ['title']  // Only highlight needed fields
})

Maintenance

Regular maintenance keeps your Meilisearch deployment running smoothly.

Index Maintenance

Monitor and manage index health:

# Check index status
curl -X GET 'http://localhost:7700/indexes' \
  -H 'Authorization: Bearer your_master_key'

# Get index information
curl -X GET 'http://localhost:7700/indexes/books' \
  -H 'Authorization: Bearer your_master_key'

Version Upgrades

Follow these steps when upgrading Meilisearch:

  1. Review release notes for breaking changes
  2. Create a backup of your data
  3. Test the upgrade in a staging environment
  4. Upgrade during low-traffic periods
  5. Monitor for any issues after upgrade
# Backup before upgrade
./meilisearch-backup.sh

# Pull new image
docker pull getmeilisearch/meilisearch:v1.13

# Recreate container with new image
docker-compose up -d meilisearch

Disk Space Management

Monitor disk usage and clean up unnecessary files:

# Check disk usage
df -h /var/lib/meilisearch

# Remove old snapshots (keep recent ones)
find /var/lib/meilisearch/snapshots -name "*.snap" -mtime +7 -delete

# Clear Meilisearch cache
curl -X DELETE 'http://localhost:7700/cache' \
  -H 'Authorization: Bearer your_master_key'

Troubleshooting

Common issues and their solutions.

High Memory Usage

If Meilisearch uses excessive memory:

  1. Reduce indexing thread count
  2. Limit document batch sizes
  3. Increase available memory
  4. Monitor index size
MEILI_MAX_INDEXING_THREADS=2
MEILI_MAX_INDEXING_MEMORY=512MiB

Slow Search Performance

For slow search queries:

  1. Check that searchable attributes are configured
  2. Verify filterable and sortable attributes are set
  3. Ensure proper indexing
  4. Monitor system resources
# Check current settings
curl -X GET 'http://localhost:7700/indexes/books/settings' \
  -H 'Authorization: Bearer your_master_key'

Indexing Stuck

If indexing appears stuck:

# Check indexing status
curl -X GET 'http://localhost:7700/indexes/books' \
  -H 'Authorization: Bearer your_master_key'

# Cancel ongoing task if needed
curl -X DELETE 'http://localhost:7700/tasks/task_id' \
  -H 'Authorization: Bearer your_master_key'

External Resources

Conclusion

Operating Meilisearch in production requires attention to deployment architecture, security, monitoring, and maintenance. This guide covered the essential operational aspects including deployment strategies, security configuration, monitoring approaches, backup procedures, and performance optimization.

By following these operational best practices, you can ensure your Meilisearch deployment is reliable, secure, and performant. Remember to regularly review and update your operational procedures as your usage grows and as new Meilisearch features become available.

In the next article, we will explore Meilisearch’s internal architecture, understanding how it achieves its remarkable search speed.

Comments