Instalación y Configuración de Meilisearch

Meilisearch es un motor de búsqueda de código abierto ultrarrápido que ofrece búsqueda instantánea con tolerancia a errores tipográficos, búsqueda en tiempo real mientras se escribe y una API REST sencilla. Diseñado para ser fácil de integrar, ofrece resultados en menos de 50ms incluso con millones de documentos, soporta búsqueda multilingüe y permite personalizar la relevancia sin conocimientos avanzados de motores de búsqueda. Esta guía cubre la instalación en Linux, gestión de índices, configuración de búsqueda y despliegue en producción.

Requisitos Previos

  • Ubuntu 20.04/22.04, Debian 11+ o CentOS 8+/Rocky Linux 8+
  • 512 MB de RAM mínimo (Meilisearch mantiene los índices en memoria)
  • Espacio en disco suficiente para los datos (los índices pueden ser 2-3x el tamaño de los datos originales)
  • Puerto 7700 disponible

Instalación de Meilisearch

Instalación con el script oficial

# Descargar e instalar el binario directamente
curl -L https://install.meilisearch.com | sh

# Verificar la instalación
./meilisearch --version

# Mover el binario al PATH del sistema
sudo mv meilisearch /usr/local/bin/

Instalación manual del binario

# Descargar la versión más reciente
MEILISEARCH_VERSION=1.7.3
wget "https://github.com/meilisearch/meilisearch/releases/download/v${MEILISEARCH_VERSION}/meilisearch-linux-amd64" \
  -O meilisearch

# Hacer el binario ejecutable
chmod +x meilisearch
sudo mv meilisearch /usr/local/bin/

# Verificar
meilisearch --version

Instalación con Docker

docker run -d \
  --name meilisearch \
  -p 7700:7700 \
  -e MEILI_MASTER_KEY="tu_clave_maestra_segura" \
  -v meilisearch-data:/meili_data \
  getmeili/meilisearch:v1.7.3

Configuración del Servidor

# Crear usuario y directorios para Meilisearch
sudo useradd -r -s /bin/false meilisearch
sudo mkdir -p /var/lib/meilisearch /etc/meilisearch /var/log/meilisearch
sudo chown -R meilisearch:meilisearch /var/lib/meilisearch /var/log/meilisearch

Crea el archivo de configuración /etc/meilisearch/config.toml:

# /etc/meilisearch/config.toml

# Directorio donde se almacenan los datos del índice
db_path = "/var/lib/meilisearch/data"

# Entorno: "development" (sin clave maestra) o "production" (clave maestra obligatoria)
env = "production"

# Clave maestra para autenticación (mínimo 16 caracteres)
master_key = "tu_clave_maestra_super_secreta_aqui"

# Puerto y dirección de escucha
http_addr = "127.0.0.1:7700"

# Directorio para dumps (exportaciones)
dump_dir = "/var/lib/meilisearch/dumps"

# Directorio para snapshots
snapshot_dir = "/var/lib/meilisearch/snapshots"
# Intervalo de snapshots automáticos (en segundos)
schedule_snapshot = 86400  # Cada 24 horas

# Nivel de log: off, error, warn, info, debug, trace
log_level = "info"

# Límite de tamaño para la base de datos LMDB (en bytes)
max_index_size = 107374182400  # 100 GB

# Tamaño máximo del payload de importación (en bytes)
http_payload_size_limit = 104857600  # 100 MB
# Crear el servicio systemd
sudo tee /etc/systemd/system/meilisearch.service > /dev/null <<EOF
[Unit]
Description=Meilisearch Search Engine
After=network.target

[Service]
Type=simple
User=meilisearch
Group=meilisearch
ExecStart=/usr/local/bin/meilisearch --config-file-path=/etc/meilisearch/config.toml
Restart=on-failure
RestartSec=5
StandardOutput=append:/var/log/meilisearch/meilisearch.log
StandardError=append:/var/log/meilisearch/meilisearch.log
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable meilisearch --now

# Verificar el estado
sudo systemctl status meilisearch

# Probar la API
curl http://localhost:7700/health

Gestión de Índices

# Variable con la clave maestra
export MEILI_KEY="tu_clave_maestra_super_secreta_aqui"
export MEILI_URL="http://localhost:7700"

# Crear un índice
curl -X POST "${MEILI_URL}/indexes" \
  -H "Authorization: Bearer ${MEILI_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "uid": "productos",
    "primaryKey": "id"
  }'

# Listar índices
curl "${MEILI_URL}/indexes" \
  -H "Authorization: Bearer ${MEILI_KEY}"

# Ver información de un índice
curl "${MEILI_URL}/indexes/productos" \
  -H "Authorization: Bearer ${MEILI_KEY}"

# Eliminar un índice
curl -X DELETE "${MEILI_URL}/indexes/productos" \
  -H "Authorization: Bearer ${MEILI_KEY}"

Indexación de Documentos

# Añadir documentos individuales o en lote
curl -X POST "${MEILI_URL}/indexes/productos/documents" \
  -H "Authorization: Bearer ${MEILI_KEY}" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": 1,
      "nombre": "Camiseta básica algodón",
      "descripcion": "Camiseta unisex de algodón 100% en varios colores",
      "categoria": "Ropa",
      "precio": 19.99,
      "stock": 150,
      "etiquetas": ["ropa", "casual", "algodón"]
    },
    {
      "id": 2,
      "nombre": "Zapatillas deportivas running",
      "descripcion": "Zapatillas ligeras para correr con amortiguación extra",
      "categoria": "Calzado",
      "precio": 89.99,
      "stock": 45,
      "etiquetas": ["deporte", "running", "calzado"]
    }
  ]'

# Las operaciones de indexación son asíncronas - verificar el estado de la tarea
curl "${MEILI_URL}/tasks" \
  -H "Authorization: Bearer ${MEILI_KEY}"

# Ver el estado de una tarea específica
curl "${MEILI_URL}/tasks/1" \
  -H "Authorization: Bearer ${MEILI_KEY}"
# Importación masiva desde un archivo JSON
curl -X POST "${MEILI_URL}/indexes/productos/documents" \
  -H "Authorization: Bearer ${MEILI_KEY}" \
  -H "Content-Type: application/json" \
  --data-binary @/tmp/productos.json

# Actualizar documentos (los campos no mencionados se mantienen)
curl -X PATCH "${MEILI_URL}/indexes/productos/documents" \
  -H "Authorization: Bearer ${MEILI_KEY}" \
  -H "Content-Type: application/json" \
  -d '[{"id": 1, "precio": 15.99, "stock": 200}]'

# Eliminar documentos por ID
curl -X DELETE "${MEILI_URL}/indexes/productos/documents/1" \
  -H "Authorization: Bearer ${MEILI_KEY}"

# Eliminar todos los documentos de un índice (sin borrar el índice)
curl -X DELETE "${MEILI_URL}/indexes/productos/documents" \
  -H "Authorization: Bearer ${MEILI_KEY}"

Búsqueda y Personalización

# Búsqueda básica
curl "${MEILI_URL}/indexes/productos/search?q=zapatillas" \
  -H "Authorization: Bearer ${MEILI_KEY}"

# Búsqueda avanzada con parámetros
curl -X POST "${MEILI_URL}/indexes/productos/search" \
  -H "Authorization: Bearer ${MEILI_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "zapatillas deporte",
    "limit": 20,
    "offset": 0,
    "filter": "categoria = Calzado AND precio < 100",
    "sort": ["precio:asc"],
    "facets": ["categoria", "etiquetas"],
    "attributesToHighlight": ["nombre", "descripcion"],
    "highlightPreTag": "<strong>",
    "highlightPostTag": "</strong>",
    "attributesToCrop": ["descripcion"],
    "cropLength": 100
  }'

Configurar atributos de búsqueda

# Definir qué campos son buscables y cuáles son filtrables
curl -X PATCH "${MEILI_URL}/indexes/productos/settings" \
  -H "Authorization: Bearer ${MEILI_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "searchableAttributes": [
      "nombre",
      "descripcion",
      "etiquetas",
      "categoria"
    ],
    "filterableAttributes": [
      "categoria",
      "precio",
      "stock",
      "etiquetas"
    ],
    "sortableAttributes": [
      "precio",
      "stock"
    ],
    "rankingRules": [
      "words",
      "typo",
      "proximity",
      "attribute",
      "sort",
      "exactness",
      "precio:asc"
    ],
    "stopWords": ["el", "la", "los", "las", "un", "una", "y", "o", "de", "del", "en"],
    "synonyms": {
      "zapatillas": ["tenis", "sneakers", "deportivas"],
      "camiseta": ["remera", "playera", "polo"]
    },
    "typoTolerance": {
      "enabled": true,
      "minWordSizeForTypos": {
        "oneTypo": 5,
        "twoTypos": 9
      }
    }
  }'

Claves de API y Seguridad

# Crear una clave de solo lectura para el frontend
curl -X POST "${MEILI_URL}/keys" \
  -H "Authorization: Bearer ${MEILI_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Clave de búsqueda pública para el sitio web",
    "actions": ["search"],
    "indexes": ["productos"],
    "expiresAt": "2025-12-31T00:00:00Z"
  }'

# Listar todas las claves
curl "${MEILI_URL}/keys" \
  -H "Authorization: Bearer ${MEILI_KEY}"

Despliegue en Producción

Configurar Nginx como proxy inverso

# /etc/nginx/sites-available/meilisearch
server {
    listen 443 ssl;
    server_name busqueda.tudominio.com;

    ssl_certificate /etc/letsencrypt/live/busqueda.tudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/busqueda.tudominio.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:7700;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;

        # Solo permitir operaciones de búsqueda desde el exterior
        # Bloquear endpoints de administración
        location ~* ^/(indexes|tasks|keys|dumps|snapshots|settings) {
            if ($request_method != GET) {
                return 403;
            }
        }
    }
}

Crear un dump (exportación de datos)

# Crear un dump para backup o migración
curl -X POST "${MEILI_URL}/dumps" \
  -H "Authorization: Bearer ${MEILI_KEY}"

# Los dumps se guardan en dump_dir configurado
ls -la /var/lib/meilisearch/dumps/

# Importar un dump al iniciar Meilisearch
meilisearch --import-dump /var/lib/meilisearch/dumps/20240115-backup.dump

Solución de Problemas

Meilisearch no arranca:

# Verificar logs
sudo journalctl -u meilisearch -n 50 --no-pager
cat /var/log/meilisearch/meilisearch.log | tail -50

# Verificar permisos del directorio de datos
ls -la /var/lib/meilisearch/
sudo chown -R meilisearch:meilisearch /var/lib/meilisearch/

Error "Invalid API key":

# En modo producción (env=production), la clave maestra es obligatoria
# Verificar que se está usando la clave correcta
curl "${MEILI_URL}/health" -H "Authorization: Bearer ${MEILI_KEY}"

# Si cambias la clave maestra, los índices existentes permanecen pero las claves anteriores dejan de funcionar

La indexación es lenta:

# Aumentar el límite de payload para importaciones masivas
# En config.toml:
http_payload_size_limit = 209715200  # 200 MB

# Importar en lotes de 50.000-100.000 documentos máximo
# Dividir archivos grandes con split o similar
split -l 50000 datos_grandes.json partes/datos_

Los resultados de búsqueda no son los esperados:

# Verificar la configuración de atributos buscables
curl "${MEILI_URL}/indexes/productos/settings" \
  -H "Authorization: Bearer ${MEILI_KEY}" | python3 -m json.tool

# Revisar las ranking rules configuradas
# Añadir sinónimos para términos del dominio
# Ajustar los pesos de los campos en searchableAttributes (el orden importa)

Conclusión

Meilisearch destaca por su facilidad de despliegue, su velocidad de búsqueda y una API intuitiva que permite a cualquier desarrollador integrar búsqueda avanzada en minutos. La configuración de atributos filtrables, ordenables y la personalización de la relevancia permiten construir experiencias de búsqueda sofisticadas sin necesidad de conocimientos especializados. Para producción, recuerda configurar siempre una clave maestra, usar un proxy inverso con HTTPS y programar dumps periódicos para la recuperación ante desastres.