Qué vas a conseguir
Pasar de "los logs están en Elasticsearch" a que te avisen: un panel que el equipo abre cada mañana, una regla que manda un mensaje cuando hay demasiados inicios de sesión fallidos, los índices con fecha de caducidad automática gracias a ILM y los data streams, y la rutina para el día en que el disco se llena y Elasticsearch se pone en solo lectura.
Antes de empezar
- Elasticsearch y Kibana 8.x funcionando, como en la guía de ELK para centralizar logs, con un usuario con permisos de administración.
- Winlogbeat en los servidores Windows y Filebeat con el módulo
systemen los Linux. En 8.x escriben en los data streamswinlogbeat-8.x.xyfilebeat-8.x.x. - En
kibana.yml, la clavexpack.encryptedSavedObjects.encryptionKeycon al menos 32 caracteres. Sin ella, Kibana no deja crear reglas ni conectores. - Licencia: con Basic solo están los conectores Index y Server log. Email, Microsoft Teams o Webhook necesitan una suscripción de pago (Gold o superior) o la prueba de 30 días.
1. Un panel para los logs de Windows y Linux
Primero las vistas de datos (data views), que dicen a Kibana qué índices leer:
Otra igual con filebeat-* para Linux. Después Analytics › Dashboards › Create dashboard › Create visualization y, con Lens, cuatro paneles:
| Panel | Vista | Filtro (KQL) | Gráfico |
|---|---|---|---|
| Inicios de sesión fallidos | Windows | event.code: 4625 | Barras por hora, top 10 de winlog.event_data.TargetUserName |
| Cuentas bloqueadas | Windows | event.code: 4740 | Tabla con hora, cuenta y host.name |
| SSH fallido en Linux | Linux | event.dataset: "system.auth" and system.auth.ssh.event: "Failed" | Top 10 de source.ip |
| Equipos que más registran | Ambas | (ninguno) | Barras por host.name |
Guarda el panel con una etiqueta (Tags: operaciones) y el intervalo Last 24 hours. El cuarto panel es el que avisa antes que nadie de un servidor con el log en modo depuración: lo vas a necesitar en el paso 6.
2. El conector
El conector es el canal por el que sale el aviso. Se crea una vez y lo usan todas las reglas:
- La URL del webhook la crea el propietario del canal en Teams. Trátala como una contraseña: quien la tenga puede escribir en el canal.
- Save & test envía un mensaje de prueba. Si no llega, el servidor de Kibana no tiene salida a Internet o necesita proxy (
xpack.actions.proxyUrlenkibana.yml). - Con licencia Basic, el conector Index escribe la alerta en un índice; útil para un panel de alertas, pero no avisa a nadie.
3. Una regla de tipo Elasticsearch query
- Test query dice cuántos documentos encuentra ahora mismo. Si devuelve miles, el umbral está mal o falta un filtro.
- On status changes manda un mensaje cuando salta y otro cuando se recupera (añade una segunda acción con Run when: Recovered), no uno cada minuto.
{{context.link}}lleva directo a los documentos en Discover: quien recibe el aviso no tiene que construir la búsqueda.- Pon nombre que diga qué pasa y dónde. "Alerta 3" a las tres de la mañana no ayuda.
4. Una política ILM con rollover y borrado
La guía anterior usaba un índice por día y una política que solo borraba. Con rollover, Elasticsearch crea un índice nuevo cuando el actual llega a un tamaño o una edad, y la fase de borrado cuenta desde ese momento. En Dev Tools:
PUT _ilm/policy/logs-contoso-30d{ "policy": { "phases": { "hot": { "actions": { "rollover": { "max_primary_shard_size": "10gb", "max_age": "1d" } } }, "delete": { "min_age": "30d", "actions": { "delete": {} } } } }}max_age: 1dymax_primary_shard_size: 10gb: lo primero que se cumpla. Con poco volumen, un índice por día; si un día se dispara, se parte antes.min_age: 30den delete se mide desde el rollover: un índice vive unos 31 días.- Los Beats y los data streams
logs-*-*usan por defecto las políticas integradas (logs,filebeat,winlogbeat), que hacen rollover pero no borran nunca. Revísalas en Stack Management › Index Lifecycle Policies y añádeles una fase de borrado, o asigna la tuya en su plantilla.
5. Un data stream propio
Un data stream es un nombre lógico (logs-contoso.servers-default) detrás del que hay índices ocultos (.ds-logs-contoso.servers-default-2026.11.03-000001). Se escribe siempre en el nombre y Elasticsearch hace el resto. Lo crea una plantilla de índice con data_stream:
PUT _index_template/logs-contoso.servers{ "index_patterns": ["logs-contoso.servers-*"], "data_stream": {}, "priority": 200, "template": { "settings": { "index.lifecycle.name": "logs-contoso-30d", "number_of_replicas": 0 } }}priority: 200, porque la plantilla integradalogs(patrónlogs-*-*, prioridad 100) también encaja con ese nombre y gana la de prioridad más alta.- Los documentos necesitan
@timestamp: el data stream lo exige.
En Logstash, la salida pasa de un índice con fecha al data stream:
output { elasticsearch { hosts => ["https://10.20.5.10:9200"] data_stream => "true" data_stream_type => "logs" data_stream_dataset => "contoso.servers" data_stream_namespace => "default" user => "logstash_writer" password => "${ES_PWD}" }}Para comprobar que la política se aplica: GET _data_stream/logs-contoso.servers-default muestra los índices y la política, y GET logs-contoso.servers-default/_ilm/explain la fase de cada uno. Un índice en ERROR en el explain es una política que no avanza y un disco que se llena.
En un data stream no se puede borrar el índice de escritura (el más reciente). Si necesitas cerrarlo, primero POST logs-contoso.servers-default/_rollover.
6. El ticket: "Kibana no muestra logs desde el sábado"
Lunes, 9:00. El panel del paso 1 está vacío desde el sábado a las 3:12. Logstash escribe esto en su log sin parar:
retrying failed action with response code: 429 ({"type"=>"cluster_block_exception", "reason"=>"index [.ds-logs-contoso.servers-default-2026.11.01-000032] blocked by: [TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block];"})Elasticsearch tiene tres marcas de disco por nodo: low (85 %, deja de asignar shards nuevos), high (90 %, intenta mover shards a otro nodo) y flood-stage (95 %). Al pasar la última pone en todos los índices de ese nodo el bloqueo index.blocks.read_only_allow_delete: se puede leer y borrar, pero no escribir.
GET _cat/allocation?v&h=node,disk.percent,disk.used,disk.availnode disk.percent disk.used disk.availsrv-elk-01 96 479.6gb 20.3gbGET _cluster/health?filter_path=status,number_of_nodes,unassigned_shards{"status":"yellow","number_of_nodes":1,"unassigned_shards":4}GET _cat/indices?v&s=store.size:desc&h=index,store.size&expand_wildcards=allindex store.size.ds-logs-contoso.servers-default-2026.11.01-000032 61.2gb.ds-logs-contoso.servers-default-2026.10.31-000031 58.7gb.ds-logs-contoso.servers-default-2026.10.04-000004 4.1gb- La salud del clúster sigue en amarillo, como siempre en un nodo único con réplicas pendientes. El bloqueo por disco no la pone en rojo: por eso nadie lo vio en el panel de estado.
- Los dos últimos índices pesan quince veces lo normal. El panel de "equipos que más registran" señala a
srv-app-07, que desde el viernes tiene la aplicación en nivel de logDEBUG.
Pasos:
- Cortar la causa: devolver el log de
srv-app-07aINFO(o parar su Filebeat mientras tanto). - Liberar espacio borrando lo más antiguo que ya no se necesita. Con el bloqueo, borrar está permitido:
DELETE .ds-logs-contoso.servers-default-2026.10.04-000004. Nunca borres ficheros a mano en el directorio de datos. - Esperar a que se levante el bloqueo. Desde la versión 7.4, Elasticsearch quita
read_only_allow_deletepor sí solo cuando el disco del nodo baja de la marca high (90 %). Comprueba conGET _cat/allocation?vque estás por debajo. - Si el disco ya está por debajo y los índices siguen bloqueados, quítalo a mano:
PUT */_settings?expand_wildcards=all{ "index.blocks.read_only_allow_delete": null}- Comprobar que entra todo: Logstash reintenta los 429 por su cuenta. Mira en Discover que llegan eventos nuevos y si hay hueco. Lo que Filebeat no pudo enviar lo reintenta desde su registro; lo que se perdió, se anota en el ticket.
Subir la marca de flood-stage al 98 % para "salir del paso" solo aplaza el problema unas horas y deja el nodo sin margen. Si lo haces, vuelve a ponerla en null en cuanto liberes espacio.
Para que no se repita: la regla de Stack Monitoring Disk Usage threshold (o una regla propia sobre el disco) avisando al 80 %, la política del paso 4 con fase de borrado en todos los data streams y un tope de volumen por servidor revisado en el panel.
En el ticket: hora de inicio del bloqueo, servidor y causa del volumen, índices borrados, hora de vuelta a la normalidad y hueco de logs si lo hubo.
Siguientes pasos
- La guía de ELK para centralizar logs cubre la instalación, el pipeline de Logstash y la búsqueda en Kibana.
- La de Grafana sirve si el equipo prefiere tener los paneles y las alertas de Elasticsearch junto a los de otras fuentes.
- Con más de un nodo, la fase warm de ILM mueve los índices viejos a discos más baratos, y las réplicas dejan el clúster en verde.
¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.
What you'll get
Going from "the logs are in Elasticsearch" to being told when something happens: a dashboard the team opens every morning, a rule that sends a message when there are too many failed logons, indices that expire on their own thanks to ILM and data streams, and the routine for the day the disk fills up and Elasticsearch goes read-only.
Before you start
- Elasticsearch and Kibana 8.x running, as in the ELK log centralisation guide, with a user that has admin rights.
- Winlogbeat on the Windows servers and Filebeat with the
systemmodule on the Linux ones. In 8.x they write to thewinlogbeat-8.x.xandfilebeat-8.x.xdata streams. - In
kibana.yml, thexpack.encryptedSavedObjects.encryptionKeykey with at least 32 characters. Without it, Kibana will not let you create rules or connectors. - Licence: with Basic only the Index and Server log connectors are available. Email, Microsoft Teams or Webhook need a paid subscription (Gold or higher) or the 30-day trial.
1. A dashboard for Windows and Linux logs
First the data views, which tell Kibana which indices to read:
Another one the same way with filebeat-* for Linux. Then Analytics › Dashboards › Create dashboard › Create visualization and, with Lens, four panels:
| Panel | Data view | Filter (KQL) | Chart |
|---|---|---|---|
| Failed logons | Windows | event.code: 4625 | Bars per hour, top 10 winlog.event_data.TargetUserName |
| Locked-out accounts | Windows | event.code: 4740 | Table with time, account and host.name |
| Failed SSH on Linux | Linux | event.dataset: "system.auth" and system.auth.ssh.event: "Failed" | Top 10 source.ip |
| Noisiest hosts | Both | (none) | Bars by host.name |
Save the dashboard with a tag (Tags: operations) and the Last 24 hours time range. The fourth panel is the one that spots a server left in debug logging before anyone else does: you will need it in step 6.
2. The connector
The connector is the channel the alert goes out through. You create it once and every rule uses it:
- The webhook URL is created by the channel owner in Teams. Treat it like a password: whoever has it can post to the channel.
- Save & test sends a test message. If it does not arrive, the Kibana server has no Internet access or needs a proxy (
xpack.actions.proxyUrlinkibana.yml). - With a Basic licence, the Index connector writes the alert into an index; useful for an alerts dashboard, but it notifies nobody.
3. An Elasticsearch query rule
- Test query tells you how many documents it finds right now. If it returns thousands, the threshold is wrong or a filter is missing.
- On status changes sends one message when it fires and another when it recovers (add a second action with Run when: Recovered), not one every minute.
{{context.link}}goes straight to the documents in Discover: whoever gets the alert does not have to build the search.- Give it a name that says what is happening and where. "Alert 3" at three in the morning helps nobody.
4. An ILM policy with rollover and delete
The earlier guide used one index per day and a policy that only deleted. With rollover, Elasticsearch creates a new index when the current one reaches a size or an age, and the delete phase counts from that moment. In Dev Tools:
PUT _ilm/policy/logs-contoso-30d{ "policy": { "phases": { "hot": { "actions": { "rollover": { "max_primary_shard_size": "10gb", "max_age": "1d" } } }, "delete": { "min_age": "30d", "actions": { "delete": {} } } } }}max_age: 1dandmax_primary_shard_size: 10gb: whichever comes first. With low volume, one index per day; if a day spikes, it splits earlier.min_age: 30din delete is measured from rollover: an index lives about 31 days.- The Beats and the
logs-*-*data streams use the built-in policies by default (logs,filebeat,winlogbeat), which roll over but never delete. Review them in Stack Management › Index Lifecycle Policies and add a delete phase, or assign yours in their template.
5. Your own data stream
A data stream is a logical name (logs-contoso.servers-default) backed by hidden indices (.ds-logs-contoso.servers-default-2026.11.03-000001). You always write to the name and Elasticsearch does the rest. It is created by an index template with data_stream:
PUT _index_template/logs-contoso.servers{ "index_patterns": ["logs-contoso.servers-*"], "data_stream": {}, "priority": 200, "template": { "settings": { "index.lifecycle.name": "logs-contoso-30d", "number_of_replicas": 0 } }}priority: 200, because the built-inlogstemplate (patternlogs-*-*, priority 100) also matches that name and the higher priority wins.- Documents need
@timestamp: the data stream requires it.
In Logstash, the output moves from a dated index to the data stream:
output { elasticsearch { hosts => ["https://10.20.5.10:9200"] data_stream => "true" data_stream_type => "logs" data_stream_dataset => "contoso.servers" data_stream_namespace => "default" user => "logstash_writer" password => "${ES_PWD}" }}To check the policy is applied: GET _data_stream/logs-contoso.servers-default shows the backing indices and the policy, and GET logs-contoso.servers-default/_ilm/explain the phase of each one. An index in ERROR in the explain output is a policy that is stuck and a disk that is filling up.
In a data stream you cannot delete the write index (the newest one). If you need to close it off, first POST logs-contoso.servers-default/_rollover.
6. The ticket: "Kibana shows no logs since Saturday"
Monday, 9:00. The dashboard from step 1 has been empty since Saturday at 3:12. Logstash keeps writing this to its log:
retrying failed action with response code: 429 ({"type"=>"cluster_block_exception", "reason"=>"index [.ds-logs-contoso.servers-default-2026.11.01-000032] blocked by: [TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block];"})Elasticsearch has three disk watermarks per node: low (85 %, stops allocating new shards), high (90 %, tries to move shards to another node) and flood-stage (95 %). Past the last one it puts the index.blocks.read_only_allow_delete block on every index on that node: you can read and delete, but not write.
GET _cat/allocation?v&h=node,disk.percent,disk.used,disk.availnode disk.percent disk.used disk.availsrv-elk-01 96 479.6gb 20.3gbGET _cluster/health?filter_path=status,number_of_nodes,unassigned_shards{"status":"yellow","number_of_nodes":1,"unassigned_shards":4}GET _cat/indices?v&s=store.size:desc&h=index,store.size&expand_wildcards=allindex store.size.ds-logs-contoso.servers-default-2026.11.01-000032 61.2gb.ds-logs-contoso.servers-default-2026.10.31-000031 58.7gb.ds-logs-contoso.servers-default-2026.10.04-000004 4.1gb- Cluster health is still yellow, as it always is on a single node with pending replicas. The disk block does not turn it red: that is why nobody saw it on the status panel.
- The last two indices are fifteen times the usual size. The "noisiest hosts" panel points at
srv-app-07, whose application has been onDEBUGlog level since Friday.
Steps:
- Stop the cause: put
srv-app-07's logging back toINFO(or stop its Filebeat in the meantime). - Free space by deleting the oldest data nobody needs. Deleting is allowed under the block:
DELETE .ds-logs-contoso.servers-default-2026.10.04-000004. Never delete files by hand in the data directory. - Wait for the block to lift. Since version 7.4, Elasticsearch removes
read_only_allow_deleteon its own once the node's disk drops below the high watermark (90 %). Check withGET _cat/allocation?vthat you are below it. - If the disk is already below it and the indices are still blocked, remove it by hand:
PUT */_settings?expand_wildcards=all{ "index.blocks.read_only_allow_delete": null}- Check everything flows in: Logstash retries the 429s on its own. Look in Discover for new events and for any gap. What Filebeat could not send it retries from its registry; anything that was lost goes in the ticket.
Raising the flood-stage watermark to 98 % to "get through it" only postpones the problem by a few hours and leaves the node with no headroom. If you do it, set it back to null as soon as you free space.
So it does not happen again: the Stack Monitoring Disk Usage threshold rule (or your own rule on disk) alerting at 80 %, the step 4 policy with a delete phase on every data stream, and a per-server volume cap reviewed on the dashboard.
In the ticket: when the block started, the server and cause of the volume, the indices deleted, when things were back to normal and any gap in the logs.
Next steps
- The ELK log centralisation guide covers the installation, the Logstash pipeline and searching in Kibana.
- The Grafana guide helps if the team prefers Elasticsearch dashboards and alerts next to those from other sources.
- With more than one node, the ILM warm phase moves old indices to cheaper disks, and replicas turn the cluster green.
Is something missing, or different in your environment? Get in touch and I'll fix it.
Tech Career Lab · newsletter
¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.Found this useful? Every two weeks I send one practical idea for IT professionals: LinkedIn, projects, networking and hiring processes.
Apúntate y llévate las 33 ideasSign up and get the 33 ideas →