Auto-hospedar Meilisearch para búsqueda rápida
Sigue esto en tu propio servidor. Empieza gratis, sin tarjeta
Algolia y otras API de búsqueda gestionadas cobran por volumen de búsquedas y por cantidad de registros, lo que se acumula rápido en cuanto un sitio tiene tráfico real. Meilisearch es un motor de búsqueda de código abierto escrito en Rust que indexa búsqueda instantánea y tolerante a errores tipográficos sobre tus propios datos, y es lo bastante liviano como para correr en el servidor más barato que vende un proveedor de nube. Al final de esta guía lo tendrás corriendo detrás de HTTPS, con una master key real protegiéndolo, un snapshot desde el cual restaurar, y una ruta de actualización corta.
Lo que necesitas
- Un servidor con Docker y el plugin de Compose. Un Hetzner CX23 (2 vCPU, 4 GB de RAM, 40 GB NVMe, 5,99 €/mes sin IVA) corre Meilisearch sin problemas para un índice pequeño o mediano.
- Un dominio o subdominio apuntado a la dirección IP del servidor.
- Acceso SSH al servidor.
- Tus datos listos para indexar: Meilisearch guarda documentos que tú le envías, no rastrea nada por su cuenta.
Paso 1: generar una master key real
Meilisearch se niega a arrancar en producción sin una. MEILI_ENV=production exige una llave de al menos 16 bytes, y sin ella (o con una más corta) el servidor registra una llave sugerida y se cierra en lugar de correr sin protección:
openssl rand -base64 48
Pon el resultado en un archivo .env al lado de tu archivo compose, sin enviarlo al control de versiones:
MEILI_MASTER_KEY=paste-the-generated-value-here
chmod 600 .env
Paso 2: escribir el archivo Docker Compose
services:
meilisearch:
image: getmeili/meilisearch:v1.55.0
restart: unless-stopped
environment:
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
MEILI_ENV: production
MEILI_NO_ANALYTICS: "true"
volumes:
- ./meili_data:/meili_data
expose:
- "7700"
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- meilisearch
volumes:
caddy_data:
caddy_config:
Esta guía fija v1.55.0, la versión estable vigente al momento de escribir esto. MEILI_ENV=production hace dos cosas: desactiva la interfaz de prueba de búsqueda integrada en la ruta raíz (una comodidad para el desarrollo local que no tiene nada que hacer accesible en un servidor público), y es lo que hace obligatoria, en lugar de opcional, la master key. MEILI_NO_ANALYTICS=true saca a la instancia del reporte de uso anónimo propio de Meilisearch. ./meili_data es un bind mount simple en lugar de un volumen nombrado específicamente para que un respaldo en el Paso 5 sea solo copiar una carpeta, y es ahí donde realmente vive cada índice y cada registro de tareas.
Verifica que se analice:
docker compose config
Paso 3: apuntar el DNS y agregar Caddy
Crea un registro A para tu subdominio apuntando a la IP del servidor, y luego agrega un Caddyfile al lado de compose.yml:
search.yourdomain.com {
reverse_proxy meilisearch:7700
}
Paso 4: arrancar la pila y crear tu primer índice
docker compose up -d
docker compose ps
curl https://search.yourdomain.com/health
Deberías ver ambos servicios en estado Up y el chequeo de salud devolver {"status":"available"}. Crea un índice y agrega un documento para confirmar que la master key realmente funciona:
curl -X POST 'https://search.yourdomain.com/indexes' \
-H "Authorization: Bearer ${MEILI_MASTER_KEY}" \
-H 'Content-Type: application/json' \
--data-binary '{ "uid": "movies", "primaryKey": "id" }'
curl -X POST 'https://search.yourdomain.com/indexes/movies/documents' \
-H "Authorization: Bearer ${MEILI_MASTER_KEY}" \
-H 'Content-Type: application/json' \
--data-binary '[{ "id": 1, "title": "Carol" }]'
Deberías recibir un taskUid en ambas llamadas. Verifica que la tarea terminó, y que una búsqueda realmente encuentra el documento:
curl "https://search.yourdomain.com/indexes/movies/search?q=carol" \
-H "Authorization: Bearer ${MEILI_MASTER_KEY}"
Deberías ver "title": "Carol" en el arreglo hits. De aquí en adelante, tu aplicación debería generar una API key con alcance limitado, no de tipo master, para cada caso de uso (una que solo pueda buscar, no gestionar índices) a través del endpoint /keys, en lugar de enviar la master key misma a un navegador.
Paso 5: respaldar /meili_data
El propio endpoint /snapshots de Meilisearch (en realidad un dump programado, no una llamada de snapshot en vivo) escribe un archivo .snapshot dentro del contenedor en un intervalo que tú configures, pero el respaldo más simple y portable es un tarball del propio volumen mientras el contenedor no esté escribiendo activamente:
cat > /opt/meilisearch/backup.sh <<'EOF'
#!/bin/sh
cd /opt/meilisearch
docker compose stop meilisearch
tar czf backups/meili-$(date +%F).tar.gz meili_data
docker compose start meilisearch
find backups -name '*.tar.gz' -mtime +14 -delete
EOF
mkdir -p /opt/meilisearch/backups
chmod +x /opt/meilisearch/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/meilisearch/backup.sh") | crontab -
Ejecútalo una vez a mano y verifica que haya funcionado: ./backup.sh && ls -lh backups. Deberías ver un archivo .tar.gz con la fecha de hoy. La breve detención y arranque mantiene el archivo internamente consistente; para un índice de búsqueda de cara al público, unos segundos de no disponibilidad a las 3 a. m. son un intercambio razonable por un respaldo en el que realmente puedas confiar. Copia el archivo fuera del servidor también, para que perder el servidor no signifique también perder el índice.
Paso 6: actualizar
docker compose pull meilisearch
docker compose up -d meilisearch
Meilisearch migra automáticamente su propio formato de datos en disco al arrancar cuando puede, pero las notas de la versión del propio proyecto señalan saltos de versión que requieren reindexar mediante un dump y una importación en lugar de una actualización en el mismo lugar; lee el registro de cambios de la versión a la que te mudas antes de un salto de varias versiones, en particular cruzando un límite de versión mayor.
El atajo: hazlo con Railyard
Railyard ofrece Meilisearch como plantilla de aplicación de un clic (meilisearch en el catálogo de plantillas), construida desde la imagen oficial getmeili/meilisearch con MEILI_NO_ANALYTICS ya definido y un volumen de datos persistente ya conectado. Despliégala directamente: app.railyard.run/deploy?template=meilisearch. Una diferencia real respecto a la ruta manual de arriba: la plantilla actualmente sigue la etiqueta flotante latest en lugar de una versión fija, así que donde esta guía te hace elegir y actualizar tú mismo una versión específica, Railyard se mueve automáticamente junto con el proyecto original; de cualquier forma define tú mismo MEILI_MASTER_KEY como variable en la página Settings de la app, no se genera por ti. Consulta desplegar con un botón para ver cómo funcionan las plantillas de un clic en general, y bases de datos y copias de seguridad para ver cómo se manejan los volúmenes persistentes en la plataforma.
Solución de problemas
El contenedor se cierra de inmediato con un mensaje sobre la master key. MEILI_ENV=production sin MEILI_MASTER_KEY definida, o con una de menos de 16 bytes, hace que Meilisearch se niegue a arrancar en lugar de correr abierto. Vuelve a revisar que el archivo .env del Paso 1 realmente tenga un valor real y que docker compose config lo muestre resuelto, no una cadena vacía.
401 Unauthorized en cada llamada a la API. Falta el encabezado Authorization: Bearer <key>, o estás pasando la llave equivocada; recuerda que la master key puede hacer todo, incluyendo generar otras llaves, pero cualquier solicitud igual la necesita (o una llave con alcance limitado) en el encabezado; no hay ninguna ruta sin autenticación una vez que se define una master key.
El HTTPS no se emite. Revisa docker compose logs caddy en busca del error de ACME; casi siempre es que el registro DNS del Paso 3 todavía no apunta a este servidor, confírmalo con dig +short search.yourdomain.com.
Los resultados de búsqueda no incluyen un documento que acabas de agregar. La indexación es asíncrona; cada escritura devuelve un taskUid, y el documento no es buscable hasta que esa tarea termina. Consulta /tasks/{taskUid} si necesitas confirmar la finalización por programa en lugar de adivinar con un retraso fijo.
Falta de memoria en un servidor pequeño con un índice grande. Meilisearch mantiene una cantidad considerable de datos del índice en memoria por velocidad de consulta; los 4 GB de un CX23 están bien para un catálogo modesto, pero un índice de varios millones de documentos quiere el CX33 (8 GB) o más grande en lugar de swap, ya que la latencia de búsqueda (no solo la estabilidad) sufre en cuanto empieza a usar swap.
Preguntas frecuentes
¿Meilisearch reemplaza a Elasticsearch? Para la mayoría de los casos de uso de búsqueda en aplicaciones (un catálogo de comercio electrónico, un sitio de documentación, búsqueda dentro de la app), sí, y con mucho menos trabajo operativo; Elasticsearch sigue ganando para agregación de logs y cargas de trabajo analíticas complejas para las que Meilisearch nunca se construyó.
¿Pueden varias apps compartir una instancia de Meilisearch? Sí, los índices tienen un espacio de nombres por uid dentro de la misma instancia, y las API keys con alcance limitado pueden restringir una llave dada a índices específicos, así que una instancia puede servir de forma segura a más de una aplicación.
¿También necesito una base de datos aparte? No, Meilisearch es el sistema de registro de lo que sea que indexes en él, aunque la mayoría de las configuraciones lo tratan como un índice de búsqueda derivado que se mantiene sincronizado con una base de datos principal, en lugar de la única copia de los datos.
¿Qué tan grande puede crecer un índice en un CX23? No hay un límite estricto en Meilisearch en sí; el techo práctico es la RAM y el disco del servidor, unos cientos de miles de documentos de tamaño típico es cómodo con 4 GB, bastante más allá de eso es donde cambiar a un servidor más grande realmente ayuda.
¿La API REST es la única forma de entrar? No, existen SDK oficiales para JavaScript, Python, PHP, Ruby, Go, Rust y más, todos los cuales llaman a la misma API REST que esta guía usa directamente con curl.
Una vez que la master key, el HTTPS y un respaldo están en su lugar, Meilisearch básicamente se mantiene solo. Las dos cosas que vale la pena revisar más adelante son una llave con alcance limitado por aplicación en lugar de la master key en todas partes, y el horario de respaldo del Paso 5 una vez que el índice sea algo que de verdad extrañarías.
Por qué eligen Railyard para esto
- Tu propia cuenta en la nube. Despliega en DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, o cualquier servidor Linux por SSH, facturado directamente por tu proveedor, sin márgenes adicionales.
- Un precio fijo por equipo. El plan Free es gratis para siempre: un servidor más un servidor de base de datos, tres apps, sin tarjeta. Pro cuesta $19 al mes por hasta cinco servidores y apps ilimitadas. Team cuesta $49 al mes con servidores ilimitados. Sin cargos por app, por despliegue, por asiento ni por gigabyte.
- Plantillas de un clic. Despliega apps de código abierto, como bases de datos, analítica y herramientas de desarrollo, en tu propio servidor con un clic.
- Bases de datos administradas. Postgres, MySQL y Redis, con copias de seguridad programadas y verificadas por restauración, y recuperación a un punto en el tiempo.
- Despliegue con git push. Despliega desde GitHub, GitLab o la CLI, con HTTPS automático y lanzamientos sin tiempo de inactividad.