Ejemplos de cron: cada minuto, hora, día, semana y mes
Ya sabes la cadencia que quieres — cada 5 minutos, entre semana a las 9, el día 1 — y solo te falta la línea. Expresiones cron listas para copiar, y los pocos casos donde lo obvio está mal.
La mayoría de las guías de cron empiezan por la expresión y te enseñan a leerla. Esta va por el otro lado: ya sabes la programación que quieres en lenguaje llano — cada cinco minutos, cada día laborable a las 9, el día 1 del mes — y solo te falta la línea para pegar. Abajo están los casos comunes como expresiones listas para copiar, agrupadas por cadencia.
Dos cosas que conviene tener presentes al copiar. La mayoría de los ejemplos usan líneas de crontab estándar de cinco campos — minuto, hora, día del mes, mes, día de la semana — y las variantes específicas de una plataforma se señalan aparte. Si quieres el porqué de la sintaxis, Cómo leer una expresión cron lo explica. Algunas necesidades habituales son justo donde lo obvio falla: «cada 35 minutos», «cada 30 segundos», «el primer lunes» o «el último día del mes». Para ellas, esta guía da el arreglo además de la línea.
Cada N minutos
* * * * * # cada minuto
*/5 * * * * # cada 5 minutos
*/15 * * * * # cada 15 minutos
*/30 * * * * # cada 30 minutos
*/N en el campo de minutos significa «cada N minutos», contando desde cero: */15 se dispara en :00, :15, :30, :45. Se comporta como esperas solo cuando N divide 60 de forma exacta — 2, 3, 4, 5, 6, 10, 12, 15, 20, 30.
La trampa: */35 no es cada 35 minutos. Un paso cuenta desde el inicio del campo y se reinicia cada hora, así que */35 se dispara en :00 y :35, luego pasa la hora y vuelve a empezar en :00 — un hueco de 35 minutos seguido de uno de 25, para siempre. Cualquier intervalo que no divida 60 hace esto (*/45 da 45 y luego 15). Para una cadencia real de 35 o 90 minutos, el campo de minutos de cron no puede expresarlo; usa un timer de systemd con intervalo fijo u otro planificador.
Cada N horas
0 * * * * # cada hora, en punto
0 */2 * * * # cada 2 horas (00:00, 02:00, 04:00, …)
0 */6 * * * # cada 6 horas (00:00, 06:00, 12:00, 18:00)
15 * * * * # cada hora, al minuto 15
Pon 0 en el campo de minutos para que la tarea se dispare una vez, en punto, y no sesenta veces durante esa hora — dejar el minuto en * mientras avanzas la hora es un desliz habitual. La regla de «divide exacto» vale igual para el rango de 24 horas: */2, */3, */4, */6, */8, */12 son uniformes; */5 (0, 5, 10, 15, 20, y luego un salto de vuelta a 0) no lo es.
Cada día a una hora fija
0 0 * * * # cada día a medianoche (00:00)
30 6 * * * # cada día a las 06:30
0 9 * * * # cada día a las 09:00
0 22 * * * # cada día a las 22:00 (10 de la noche)
Primero el minuto, luego la hora, ambos en formato de 24 horas. 30 6 es las 06:30; dale la vuelta a 6 30 y es un error, porque la hora 30 no existe. Aquí la posición es todo el significado — no hay etiquetas.
Varias veces al día
0 9,17 * * * # dos veces al día, a las 09:00 y 17:00
0 0,12 * * * # dos veces al día, a las 00:00 y 12:00
0 8-18 * * * # en punto, de 08:00 a 18:00
0 9-17/2 * * * # cada 2 horas de 09:00 a 17:00 (9, 11, 13, 15, 17)
Una coma enumera valores concretos; un rango con paso (9-17/2) recorre el rango a saltos. El final del rango es inclusivo: 8-18 incluye las 18:00, y 9-17/2 incluye las 17:00.
Una franja que cruza la medianoche
0 22-23,0-6 * * * # cada hora de 22:00 a 06:00
Un rango solo cuenta hacia arriba, así que 22-6 no es una franja nocturna válida — cron no la envuelve alrededor de la medianoche y, según el parser, o da error o no coincide con nada. Pártela en dos rangos que se juntan en la medianoche: 22-23 y 0-6. Lo mismo con cualquier envoltura: «de viernes a lunes» no es 5-1 sino 5,6,0,1.
Días concretos de la semana
0 9 * * 1-5 # entre semana (lun–vie) a las 09:00
0 9 * * 1 # cada lunes a las 09:00
0 9 * * 0 # cada domingo a las 09:00 (0 = domingo)
0 9 * * 6,0 # fines de semana (sáb + dom) a las 09:00
0 9 * * MON-FRI # entre semana, con nombres
La trampa: los números de los días de la semana no son portables. El cron estándar cuenta 0–7, con 0 y 7 ambos domingo. Pero Cloudflare Workers cuenta 1=domingo hasta 7=sábado, así que allí 1-5 es de domingo a jueves, no de lunes a viernes; Kubernetes trata 0=domingo. Escribe los días con nombres — MON-FRI, SUN — donde tu plataforma los acepte, y la numeración deja de importar. (Más sobre las diferencias de dialecto en la guía de lectura.)
Horario laboral: cada N minutos, solo entre semana
*/15 9-17 * * 1-5 # cada 15 min, de 09:00 a 17:xx, lun–vie
0 9-17 * * 1-5 # en punto, de 09:00 a 17:00, lun–vie
La programación compuesta más habitual, leída de izquierda a derecha: cada 15 minutos, durante las horas 9 a 17, entre semana. Un matiz que conviene prever: el final del rango horario es inclusivo, así que 9-17 cubre toda la hora de las 17 — la tarea sigue disparándose a las 17:15, 17:30, 17:45 y solo para a las 18:00. Si la quieres terminar a las 17:00 en punto, recorta el rango del paso a 9-16 y añade una línea para las 17 en punto: 0 17 * * 1-5.
Mensual, y días concretos del mes
0 0 1 * * # el día 1 de cada mes a medianoche
0 0 15 * * # el día 15 de cada mes
0 9 1 1,7 * # el 1 de enero y el 1 de julio a las 09:00 (dos veces al año)
0 0 1 */3 * # el primer día de cada trimestre (ene, abr, jul, oct)
Aquí viven tres trampas. Primera, no hay un campo sencillo para «el último día del mes» — el día del mes es un número fijo y los meses varían de longitud. L lo expresa (0 0 L * *), pero L es una extensión de dialecto (Quartz y algún otro), no cron estándar, así que confirma que tu plataforma lo admite; la salida portable está en la siguiente sección.
Segunda, la regla del «O» entre día del mes y día de la semana: si restringes ambos campos, el cron estándar se ejecuta cuando coincide cualquiera de los dos, no los dos a la vez. Así que 0 0 13 * FRI significa «el día 13 de cada mes, y además cada viernes» — no «viernes 13». Deja uno de los dos campos de día en * salvo que de verdad quieras un O.
Tercera, un día que un mes no tiene sencillamente no se ejecuta ese mes. 0 0 31 * * corre solo en los siete meses de 31 días — se salta febrero, abril, junio, septiembre y noviembre — y 0 0 30 2 * (30 de febrero) no corre nunca. Para «fin de mes» sin importar la longitud, no fijes 30 ni 31; usa la guarda de la siguiente sección.
El enésimo día de la semana del mes
«Primer lunes», «último viernes» — habituales para informes mensuales y ventanas de mantenimiento, y un sitio donde la regla del O muerde. El tentador 0 9 1-7 * 1 está mal: con los campos de día del mes (1-7) y de día de la semana (1) ambos puestos, cron los combina con O, así que se dispara todos los días del 1 al 7 y cada lunes. El modismo portable es programar la ventana de días y decidir el día de la semana dentro de la tarea:
# Primer lunes a las 09:00 — del 1 al 7 siempre hay exactamente un lunes
0 9 1-7 * * [ "$(date +\%u)" = 1 ] && /srv/app/monthly.sh
date +%u es el día de la semana ISO (1 = lunes … 7 = domingo); la guarda deja que la tarea siga solo el lunes que cae entre los días 1 y 7. Fíjate en el % escapado como \%: un % sin escapar en un comando de crontab hace que cron corte el comando en ese punto (¿Por qué no se ejecutó mi tarea cron? cubre esa trampa). Para «último viernes», corre cada viernes y deja que el script compruebe si el próximo viernes cae en otro mes. Y si tu plataforma es Quartz, el dialecto lo dice directo: MON#1 para el primer lunes, 6L para el último viernes.
Menos de un minuto: «cada 30 segundos»
La unidad más pequeña del cron estándar es un minuto. No hay campo de segundos, así que «cada 30 segundos» no se puede escribir como una línea de cinco campos. Hay dos formas de aproximarlo dentro de cron y una alternativa mejor cuando importa la fiabilidad:
# 1. Un cron de seis campos (Spring, muchas librerías) pone los segundos primero:
*/30 * * * * * # cada 30 segundos, donde se admiten seis campos
# 2. Dos líneas de cinco campos, una desfasada con un sleep:
* * * * * /path/to/job
* * * * * sleep 30 && /path/to/job
La forma de seis campos solo funciona en planificadores que aceptan un campo de segundos — cuenta los campos, seis significa que el primero son segundos — y un crontab clásico de Unix (cinco campos) la rechaza. El par con sleep 30 es un apaño pragmático para un crontab real de un minuto, no un intervalo preciso: las dos invocaciones son independientes y pueden solaparse si una ejecución dura más de 30 segundos. Para algo genuinamente por debajo del minuto y que deba ser fiable, un timer de systemd con OnUnitActiveSec=30s, o un proceso de larga vida con su propio bucle, encaja mejor que cron.
Atajos con nombre
Los crontab al estilo Cronie y Vixie aceptan unos cuantos atajos con nombre en lugar de los cinco campos:
@hourly # 0 * * * *
@daily # 0 0 * * * (@midnight es lo mismo)
@weekly # 0 0 * * 0
@monthly # 0 0 1 * *
@yearly # 0 0 1 1 * (@annually es lo mismo)
@reboot # una vez, cuando arranca el demonio
Se leen bien, pero son una extensión que solo algunos crontab admiten — los planificadores gestionados como GitHub Actions y Cloudflare quieren la forma de cinco campos. @reboot en particular necesita una máquina con ciclo de arranque, así que las plataformas serverless no lo ejecutan. Ante la duda, usa la línea explícita de cinco campos que el atajo representa.
Cuando los campos de cron no pueden decirlo
Hay condiciones de calendario que sencillamente no caben en cinco campos: el último día del mes, el primer lunes o cada día laborable menos festivos. El patrón es siempre el mismo — programa la tarea más a menudo de lo que necesitas y deja que la propia tarea decida si se ejecuta. Pon la condición que cron no puede expresar en una guarda al principio de un script envoltorio y sal pronto cuando no sea el momento:
0 0 * * * /srv/app/eom.sh # córrelo a diario…
#!/bin/bash
# eom.sh — sigue solo el último día del mes
[ "$(date -d tomorrow +%d)" = "01" ] || exit 0
# … el trabajo de verdad …
El mismo patrón resuelve el resto: corre cada viernes y sal salvo que el próximo viernes sea de otro mes (último viernes); corre cada día laborable y sal en las fechas de una lista de festivos. Para un intervalo fijo, como 90 minutos, no lo fuerces con una guarda que corre cada hora: usa un planificador de intervalos fijos. Cron lleva la tarea a la franja horaria aproximada; el script toma la decisión final. (date -d es de GNU coreutils; en BSD o macOS usa date -v+1d +%d.)
Comprueba antes de desplegar
Las expresiones de cinco campos de arriba son cron estándar, pero esta guía también señala extensiones específicas de cada plataforma, como L, un campo de segundos y los atajos con nombre. Incluso la misma expresión de cinco campos puede significar cosas distintas según la plataforma: la numeración de los días y la zona horaria de ejecución son dos ejemplos. Dos hábitos atrapan la mayoría de los errores:
- Léela de vuelta en una herramienta. Pega la expresión en el lector de expresiones cron: imprime la programación en lenguaje claro y lista las próximas horas de ejecución en la zona que elijas, de modo que «cada 15 minutos entre semana» se confirma en lugar de suponerse. (Expande primero un atajo tipo
@dailya su forma de cinco campos — el lector analiza campos numéricos, no las macros.) - Cuida la zona horaria. Una línea de cron no lleva zona horaria propia;
0 9 * * *son «las 9:00» en la zona que use el runtime — local del servidor, o UTC en la mayoría de las plataformas gestionadas. Si eso importa, el lector muestra cada ejecución en la zona que elijas. Para comparar el desfase del servidor con el tuyo, usa el conversor de zonas horarias; UTC, GMT, ISO 8601 y la hora Unix desenreda el porqué.
Y si una línea que ya has confirmado correcta sigue sin ejecutarse, esa es la otra mitad de la historia — el entorno, dónde está el crontab, o el propio host — y está en ¿Por qué no se ejecutó mi tarea cron?.
Referencia rápida
| Lo que quieres | La línea |
|---|---|
| Cada minuto | * * * * * |
| Cada 5 minutos | */5 * * * * |
| Cada 15 minutos | */15 * * * * |
| Cada hora, en punto | 0 * * * * |
| Cada 2 horas | 0 */2 * * * |
| Cada día a medianoche | 0 0 * * * |
| Cada día a las 09:00 | 0 9 * * * |
| Dos veces al día (09:00, 17:00) | 0 9,17 * * * |
| De noche, cada hora (22:00–06:00) | 0 22-23,0-6 * * * |
| Entre semana a las 09:00 | 0 9 * * 1-5 |
| Cada lunes a las 09:00 | 0 9 * * 1 |
| Fines de semana a las 09:00 | 0 9 * * 6,0 |
| Horario laboral, cada 15 min | */15 9-17 * * 1-5 |
| Día 1 del mes, medianoche | 0 0 1 * * |
| Primer día de cada trimestre | 0 0 1 */3 * |
| Primer lunes, 09:00 | 0 9 1-7 * * + guarda de día |
| Último día del mes | a diario + guarda date |