¿Qué es una expresión cron y cómo se lee?
Los cinco campos de una expresión cron, los cuatro símbolos que los combinan, ejemplos de tareas de negocio y los errores que hacen que una tarea se ejecute a otra hora o en otros días.
Por Javier Tenjo, Desarrollador full stack
En esta página
Casi toda automatización tiene una parte que se repite: el informe de ventas que llega cada lunes, la copia de seguridad de cada noche, la sincronización de pedidos cada cuarto de hora. Y todas necesitan que alguien diga cuándo.
En los servidores Linux, y en muchas plataformas que los imitan (GitHub Actions, Kubernetes, los disparadores programados de Cloudflare Workers y buena parte de las herramientas de automatización), ese «cuándo» se escribe con la misma notación: una expresión cron. Cinco campos separados por espacios, como 0 8 * * 1, que se leen mal la primera vez y con soltura a la tercera.
Cron es el planificador de tareas de Unix desde los años setenta, y su notación sobrevivió porque es compacta: en una línea cabe «cada quince minutos, de lunes a viernes, en horario de oficina».
Los cinco campos
┌───────── minuto (0–59)
│ ┌─────── hora (0–23)
│ │ ┌───── día del mes (1–31)
│ │ │ ┌─── mes (1–12)
│ │ │ │ ┌─ día de la semana (0–7; 0 y 7 son domingo)
│ │ │ │ │
0 8 * * 1
La expresión del ejemplo se lee así: minuto 0, hora 8, cualquier día del mes, cualquier mes, los lunes. Es decir, cada lunes a las 8:00.
La primera trampa está en el orden: el minuto va antes que la hora. 30 8 * * * son las 8:30, no las 30:08. Es lo contrario de como escribimos una hora, y por eso es el error más habitual al escribir una expresión de memoria.
Los cuatro símbolos
Con un número suelto en cada campo solo se programa una hora fija. El resto sale de cuatro símbolos:
*(asterisco): todos los valores. En el campo de hora significa «a cualquier hora».,(coma): una lista.0 9,13,17 * * *es a las 9, a las 13 y a las 17.-(guion): un rango, extremos incluidos.1-5en el día de la semana es de lunes a viernes./(barra): un paso.*/15en el minuto es cada quince minutos: en el 0, el 15, el 30 y el 45.
Se combinan entre sí. */15 8-18 * * 1-5 es cada quince minutos, de 8 a 18, de lunes a viernes. Un matiz de ese ejemplo: el rango 8-18 incluye toda la hora 18, así que la última ejecución del día es a las 18:45, no a las 18:00.
El paso cuenta dentro del rango del campo, no desde la última ejecución. Por eso un paso que no divide el rango de forma exacta deja el último intervalo de cada ciclo más corto que los demás.
Ejemplos de tareas de negocio
| Tarea | Expresión |
|---|---|
| Informe semanal, cada lunes a las 8:00 | 0 8 * * 1 |
| Copia de seguridad cada noche a las 2:30 | 30 2 * * * |
| Sincronizar pedidos cada 15 minutos en horario laboral | */15 8-18 * * 1-5 |
| Recordatorio de facturación el día 1 de cada mes a las 9:00 | 0 9 1 * * |
| Cierre trimestral, el primer día de cada trimestre a las 7:00 | 0 7 1 1,4,7,10 * |
| Limpieza de archivos temporales cada domingo a medianoche | 0 0 * * 0 |
Hay cosas que cron no sabe decir, y conviene saberlo antes de intentarlo. El último día del mes es la más pedida: no hay forma de escribirla en el cron clásico. La solución habitual es programar la tarea los días 28 a 31 y que el propio script compruebe si mañana es día 1 antes de hacer nada.
Si estás decidiendo qué tareas de tu empresa merecen programarse, en qué automatizar primero hay un criterio para elegir.
La zona horaria: el error que más se repite
Una expresión cron no dice en qué zona horaria está. Se ejecuta en la del sistema que la lee, y la mayoría de los servidores y de las plataformas en la nube trabajan en UTC.
Si escribes 0 8 * * 1 pensando en las ocho de la mañana en Bogotá, en un servidor en UTC la tarea se ejecuta a las tres de la madrugada hora de Bogotá. Colombia está a UTC−5 todo el año, así que la corrección es fija: para las 8:00 de Bogotá hay que escribir 0 13 * * 1.
En los países con horario de verano la cosa se complica, porque la diferencia con UTC cambia dos veces al año. Una tarea programada en UTC se mueve una hora en el reloj local cada vez que cambia la hora; una programada en la hora local puede, la noche del cambio, saltarse una ejecución o hacer dos si cae en la franja que se adelanta o se repite.
Tres costumbres evitan casi todos los sustos:
- Comprueba en qué zona trabaja la plataforma antes de escribir la expresión. Algunas permiten declarar la zona junto al horario; otras solo aceptan UTC.
- Deja escrita la zona junto a la expresión, en un comentario o en la documentación de la tarea. Quien la lea dentro de un año no tendrá que adivinarla.
- Evita la franja entre la 1:00 y las 3:00 para tareas críticas si trabajas en hora local de una zona con cambio de hora.
Día del mes y día de la semana a la vez
Esta es la regla de cron que más gente descubre en producción. Cuando el día del mes y el día de la semana están los dos restringidos, cron ejecuta si se cumple cualquiera de los dos, no los dos a la vez.
0 9 1 * 1 no significa «el día 1 si cae en lunes». Significa «el día 1 de cada mes y, además, todos los lunes». La tarea se ejecuta unas cinco veces al mes en vez de una vez cada tantos meses.
Para pedir la intersección, deja uno de los dos campos en asterisco y comprueba el otro dentro del script. Por ejemplo, para «el primer lunes de cada mes»: programa 0 9 * * 1 y que el script salga sin hacer nada si el día del mes es mayor que 7.
Otros errores frecuentes
- Cinco campos o seis. Algunas herramientas usan el formato de Quartz, que añade los segundos al principio, y algunos servicios en la nube añaden el año al final. Una expresión copiada de un sistema a otro puede desplazar todos sus campos una posición.
- La numeración de los días. En el cron clásico el 0 es domingo y el 1 es lunes. En Quartz, el 1 es domingo. El mismo número, un día de diferencia.
- Comodines que tu servidor no entiende.
L,Wy#(último día, día laborable más cercano, enésimo día de la semana) son de Quartz y de algunas variantes, no del crontab de un servidor Linux. - Suponer que se ejecutó. Cron no avisa si una tarea falla. Guarda un registro de cada ejecución y configura una alerta cuando algo no termina bien, o te enterarás cuando alguien pregunte por el informe.
- Tareas que se solapan. Si una sincronización cada cinco minutos tarda siete, la siguiente arranca antes de que acabe la anterior. Hay que evitar que corran dos a la vez o espaciar el horario.
- Más frecuencia de la necesaria. Consultar una API externa cada minuto agota cuotas y no aporta nada si los datos cambian cada hora.
¿Cómo comprobar la tuya?
El generador de expresiones cron te deja escribir la expresión entera, rellenar los cinco campos por separado o partir de un ejemplo habitual. A cada cambio la describe en lenguaje corriente y calcula las próximas cinco ejecuciones en tu hora y en UTC, que es la comprobación que descubre la mayoría de los errores de zona. Avisa también cuando restringes a la vez el día del mes y el de la semana, cuando un paso no divide su rango y cuando la expresión no se ejecutaría nunca, y reconoce las expresiones de seis campos de Quartz en lugar de interpretarlas mal.
Cron es una notación de cinco campos que se aprende en una tarde y se usa durante años. Lo que la hace traicionera no es la sintaxis, sino lo que no se ve: la zona del servidor y la regla del «o» entre días. Comprobar las próximas fechas antes de dar una tarea por buena es lo que separa una automatización fiable de una que falla un lunes sin que nadie lo note.