Elasticsearch es un motor de búsqueda y análisis distribuido, basado en Apache Lucene, que indexa documentos JSON y permite hacer búsquedas de texto completo y agregaciones sobre millones de registros en milisegundos. En este tutorial instalarás Elasticsearch 9 en un único nodo con Ubuntu 24.04 desde el repositorio oficial de Elastic, con la seguridad (autenticación y TLS) que el paquete configura automáticamente. Después crearás un índice, harás búsquedas y darás de alta un usuario con permisos limitados para tu aplicación.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS de 64 bits, por ejemplo un VPS de CubePath.
- Al menos 4 GB de RAM y 2 vCPU. Elasticsearch reserva por defecto la mitad de la RAM para la JVM; con menos memoria funciona para pruebas, pero no para producción.
- Espacio en disco para tus datos más un margen del 20 %: Elasticsearch deja de asignar datos cuando el disco supera el 85 % de ocupación.
- Un usuario no root con privilegios
sudo. - UFW activo permitiendo SSH (
sudo ufw allow OpenSSHysudo ufw enable).
No necesitas instalar Java: el paquete incluye su propio JDK.
Paso 1: Añadir el repositorio oficial de Elastic
Instala las herramientas para descargar y convertir la clave del repositorio:
sudo apt update
sudo apt install curl gnupg apt-transport-https
Descarga la clave GPG de Elastic en /etc/apt/keyrings:
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /etc/apt/keyrings/elasticsearch.gpg
Añade el repositorio de la rama 9.x firmado con esa clave:
echo "deb [signed-by=/etc/apt/keyrings/elasticsearch.gpg] https://artifacts.elastic.co/packages/9.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-9.x.list
Actualiza el índice y comprueba que el paquete viene de artifacts.elastic.co:
sudo apt update
apt-cache policy elasticsearch
elasticsearch:
Installed: (none)
Candidate: 9.1.4
Version table:
9.1.4 500
500 https://artifacts.elastic.co/packages/9.x/apt stable/main amd64 Packages
La versión que veas será la última publicada de la rama 9.
Paso 2: Instalar Elasticsearch y guardar la contraseña de elastic
Instala el paquete:
sudo apt install elasticsearch
Durante la instalación el paquete activa la seguridad automáticamente: genera certificados TLS para la API HTTP y para la comunicación entre nodos, y crea una contraseña para el superusuario elastic. Busca este bloque en la salida:
--------------------------- Security autoconfiguration information ------------------------------
Authentication and authorization are enabled.
TLS for the transport and HTTP layers is enabled and configured.
The generated password for the elastic built-in superuser is : your_elastic_password
Importantecopia esa contraseña a tu gestor de contraseñas ahora; no se vuelve a mostrar. Si la pierdes, puedes generar otra con el comando del paso 4.
Paso 3: Configurar el nodo
La configuración principal está en /etc/elasticsearch/elasticsearch.yml. Cambia el nombre del clúster y del nodo antes del primer arranque, porque el nombre del clúster queda asociado a los datos:
sudo nano /etc/elasticsearch/elasticsearch.yml
Descomenta y ajusta estas líneas al principio del archivo:
cluster.name: mi-cluster
node.name: es01
No toques el bloque que empieza por BEGIN SECURITY AUTO CONFIGURATION al final del archivo: contiene las rutas de los certificados, cluster.initial_master_nodes con el nombre de tu servidor y http.host: 0.0.0.0. Esta última línea hace que la API escuche en todas las interfaces; el acceso lo controlarás con el cortafuegos en el paso 7.
Los datos se guardan en /var/lib/elasticsearch y los logs en /var/log/elasticsearch. Si quieres los datos en otro disco, cambia path.data en este mismo archivo.
Fijar el tamaño del heap (opcional)
Elasticsearch calcula el heap de la JVM según la RAM del servidor, lo cual es adecuado si el servidor solo ejecuta Elasticsearch. Si comparte máquina con otros servicios, fíjalo tú. Crea un archivo en jvm.options.d (no edites jvm.options directamente):
sudo nano /etc/elasticsearch/jvm.options.d/heap.options
-Xms2g
-Xmx2g
Usa el mismo valor en ambas líneas, como máximo la mitad de la RAM y nunca por encima de 31 GB.
Paso 4: Arrancar Elasticsearch
El paquete no arranca el servicio automáticamente. Recarga systemd, habilita el servicio y arráncalo:
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch
El arranque tarda entre 20 y 60 segundos. Comprueba el estado:
sudo systemctl status elasticsearch --no-pager
● elasticsearch.service - Elasticsearch
Loaded: loaded (/usr/lib/systemd/system/elasticsearch.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-25 10:31:07 UTC; 25s ago
Si no guardaste la contraseña de elastic en el paso 2, genera una nueva con el servicio en marcha:
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
Paso 5: Comprobar el acceso por HTTPS
La API solo acepta HTTPS con el certificado que firmó la CA generada en la instalación. Copia el certificado de la CA a tu directorio personal para usarlo con curl sin sudo (es un certificado público, no una clave):
sudo cp /etc/elasticsearch/certs/http_ca.crt ~/http_ca.crt
sudo chown "$USER": ~/http_ca.crt
Carga la contraseña de elastic en una variable sin que quede en el historial; read -rs la pide sin mostrarla:
read -rs ELASTIC_PASSWORD && export ELASTIC_PASSWORD
Haz una petición a la raíz de la API:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" https://localhost:9200
{
"name" : "es01",
"cluster_name" : "mi-cluster",
"cluster_uuid" : "hR4Pz9xUQ3ivLr0nq0V8bA",
"version" : {
"number" : "9.1.4",
...
},
"tagline" : "You Know, for Search"
}
Comprueba la salud del clúster:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" "https://localhost:9200/_cluster/health?pretty"
{
"cluster_name" : "mi-cluster",
"status" : "green",
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
...
}
Paso 6: Crear un índice y buscar documentos
Crea un índice productos con un mapeo explícito. Como solo hay un nodo, pon number_of_replicas a 0: con el valor por defecto (1) la réplica no se podría asignar y el clúster quedaría en estado yellow:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" -X PUT "https://localhost:9200/productos?pretty" \
-H "Content-Type: application/json" -d '
{
"settings": { "number_of_shards": 1, "number_of_replicas": 0 },
"mappings": {
"properties": {
"nombre": { "type": "text" },
"categoria": { "type": "keyword" },
"precio": { "type": "float" }
}
}
}'
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "productos"
}
text se analiza para búsqueda de texto completo; keyword se guarda tal cual y sirve para filtros exactos y agregaciones.
Indexa varios documentos de una vez con la API _bulk, que usa formato NDJSON (una línea de acción y otra de documento). refresh=wait_for espera a que los documentos sean visibles en las búsquedas:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" -X POST "https://localhost:9200/productos/_bulk?refresh=wait_for" \
-H "Content-Type: application/x-ndjson" --data-binary '
{ "index": {} }
{ "nombre": "Portátil ligero de 14 pulgadas", "categoria": "portatiles", "precio": 899 }
{ "index": {} }
{ "nombre": "Portátil gaming de 16 pulgadas", "categoria": "portatiles", "precio": 1499 }
{ "index": {} }
{ "nombre": "Monitor de 27 pulgadas 4K", "categoria": "monitores", "precio": 349 }
'
La respuesta debe contener "errors":false.
Busca los productos que contienen "portátil" con un precio máximo de 1000:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" "https://localhost:9200/productos/_search?pretty" \
-H "Content-Type: application/json" -d '
{
"query": {
"bool": {
"must": { "match": { "nombre": "portátil" } },
"filter": { "range": { "precio": { "lte": 1000 } } }
}
}
}'
"hits" : {
"total" : { "value" : 1, "relation" : "eq" },
"hits" : [
{
"_index" : "productos",
"_score" : 0.5,
"_source" : {
"nombre" : "Portátil ligero de 14 pulgadas",
"categoria" : "portatiles",
"precio" : 899
}
}
]
}
Cuenta los productos y el precio medio por categoría con una agregación. "size": 0 omite los documentos y devuelve solo el resultado agregado:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" "https://localhost:9200/productos/_search?pretty" \
-H "Content-Type: application/json" -d '
{
"size": 0,
"aggs": {
"por_categoria": {
"terms": { "field": "categoria" },
"aggs": { "precio_medio": { "avg": { "field": "precio" } } }
}
}
}'
"por_categoria" : {
"buckets" : [
{ "key" : "portatiles", "doc_count" : 2, "precio_medio" : { "value" : 1199.0 } },
{ "key" : "monitores", "doc_count" : 1, "precio_medio" : { "value" : 349.0 } }
]
}
Paso 7: Crear un usuario para tu aplicación y limitar el acceso
El superusuario elastic es para administración; tu aplicación debe usar un usuario con permisos solo sobre sus índices. Crea un rol que permita leer y escribir en los índices que empiezan por productos:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" -X PUT "https://localhost:9200/_security/role/productos_rw?pretty" \
-H "Content-Type: application/json" -d '
{
"indices": [
{ "names": ["productos*"], "privileges": ["read", "write", "create_index", "view_index_metadata"] }
]
}'
Crea el usuario app_productos con ese rol. Sustituye your_app_password por una contraseña larga, por ejemplo una generada con openssl rand -base64 24:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" -X POST "https://localhost:9200/_security/user/app_productos?pretty" \
-H "Content-Type: application/json" -d '
{
"password": "your_app_password",
"roles": ["productos_rw"]
}'
{
"created" : true
}
Comprueba que el nuevo usuario puede buscar en productos pero no consultar la configuración del clúster:
curl --cacert ~/http_ca.crt -u "app_productos:your_app_password" "https://localhost:9200/productos/_count"
curl --cacert ~/http_ca.crt -u "app_productos:your_app_password" "https://localhost:9200/_cluster/settings"
La primera petición devuelve {"count":3,...} y la segunda un error security_exception con estado 403.
Por último, decide quién puede llegar al puerto 9200. Con UFW activo y sin reglas para ese puerto, la API solo es accesible desde el propio servidor. Si tu aplicación corre en otro servidor, permite únicamente su IP (sustituye app_server_ip), idealmente a través de una red privada:
sudo ufw allow from app_server_ip to any port 9200 proto tcp
sudo ufw status
No abras el puerto 9300: es el de comunicación entre nodos y en un nodo único no se usa desde fuera.
Solución de problemas
El servicio no arranca o se detiene al poco. Consulta el journal y el log del clúster, que se llama como cluster.name:
sudo journalctl -u elasticsearch -n 50 --no-pager
sudo tail -n 50 /var/log/elasticsearch/mi-cluster.log
Si el sistema lo mata por falta de memoria (Out of memory: Killed process en sudo dmesg), reduce el heap como en el paso 3 o amplía la RAM del servidor.
curl: (52) Empty reply from server. Estás usando http:// en lugar de https://. La API solo acepta TLS.
curl: (60) SSL certificate problem. Falta --cacert ~/http_ca.crt o la ruta es incorrecta. No uses -k para saltarte la verificación salvo en una prueba puntual.
security_exception con estado 401. Usuario o contraseña incorrectos. Comprueba que la variable ELASTIC_PASSWORD está cargada en la sesión actual o restablece la contraseña con elasticsearch-reset-password.
Los índices pasan a solo lectura (index read-only / allow delete). El disco ha superado el 95 % de ocupación y Elasticsearch bloquea las escrituras para protegerse. Libera espacio o amplía el disco; el bloqueo se levanta automáticamente cuando la ocupación baja del umbral. Comprueba el uso por nodo con:
curl --cacert ~/http_ca.crt -u "elastic:$ELASTIC_PASSWORD" "https://localhost:9200/_cat/allocation?v"
Conclusión
Tienes Elasticsearch 9 funcionando en Ubuntu 24.04 con autenticación y TLS, un índice con mapeo explícito, búsquedas y agregaciones, y un usuario con permisos mínimos para tu aplicación. Como siguientes pasos puedes instalar Kibana y conectarlo con un token de inscripción (elasticsearch-create-enrollment-token -s kibana), configurar instantáneas periódicas con un repositorio de snapshots y usar el cliente oficial de Elasticsearch para tu lenguaje en lugar de curl.
