Expresiones cron explicadas: campos, pasos y zonas horarias
Cron es el programador que ejecuta órdenes a horas fijas en los sistemas tipo Unix, y su formato de cinco campos se ha extendido mucho más allá: GitHub Actions, Kubernetes y muchos paneles de alojamiento usan la misma notación. Una línea como 30 2 * * 1 se lee «a las 02:30 todos los lunes». Casi todo el formato es sencillo; unas pocas reglas no lo son, y son las que hay detrás de las tareas que se ejecutan a la hora equivocada, dos veces o nunca. Esta guía sigue el estándar POSIX y cronie, un cron muy usado en Linux, e indica en qué difieren otros programadores.
Los cinco campos
Una línea de crontab tiene cinco campos de tiempo separados por espacios y, después, la orden. La tarea se ejecuta en cada minuto que cumple todos ellos, con una excepción para los dos campos de día que se explica más abajo. El estándar numera los días de la semana del 0 al 6, con el 0 para el domingo; cronie y la mayoría de los cron de Linux aceptan también el 7 para el domingo, y nombres ingleses de tres letras como mon o jan, en mayúsculas o minúsculas.
| Campo | Valores | Ejemplo | Significa |
|---|---|---|---|
| Minuto | 0–59 | 30 | en el minuto 30 |
| Hora | 0–23 | 2 | de 02:00 a 02:59 |
| Día del mes | 1–31 | 1,15 | los días 1 y 15 |
| Mes | 1–12 o jan–dec | * | todos los meses |
| Día de la semana | 0–7 o sun–sat (0 y 7 son domingo) | 1-5 | de lunes a viernes |
Un asterisco significa todos los valores del campo. Las horas usan el reloj de 24 horas: 9 son las 09:00 y 21 las 21:00.
Listas, rangos y pasos
Un campo puede llevar una lista (1,15), un rango (9-17) o un paso. El paso divide un rango: 0-30/10 en el campo de minutos son los minutos 0, 10, 20 y 30, y */15 recorre todos los valores desde el primero, es decir, los minutos 0, 15, 30 y 45. Los pasos se cuentan desde el inicio del rango y vuelven a empezar en cada hora, día o mes nuevo; no continúan desde el anterior.
*/15 * * * *Cada 15 minutos: en los minutos 0, 15, 30 y 45.0 9-17 * * 1-5A la hora en punto de 09:00 a 17:00, de lunes a viernes.0 0 */7 * *A medianoche los días 1, 8, 15, 22 y 29: la cuenta empieza de nuevo cada mes.*/25 * * * *En los minutos 0, 25 y 50, así que entre las :50 y las :00 siguientes hay 10 minutos, no 25.
En el campo de minutos solo dan intervalos iguales los pasos que dividen 60 exactamente (1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30). Cada 90 minutos hacen falta dos líneas: 0 0-21/3 * * * se ejecuta a las 00:00, 03:00 … 21:00, y 30 1-22/3 * * * a las 01:30, 04:30 … 22:30.
cronie acepta también atajos: @hourly, @daily (0 0 * * *), @weekly (0 0 * * 0), @monthly (0 0 1 * *), @yearly o @annually (0 0 1 1 *) y @reboot, que se ejecuta una vez al arrancar. No forman parte del estándar: GitHub Actions, por ejemplo, no los acepta.
Día del mes o día de la semana
Cuando los campos de día del mes y de día de la semana tienen algo distinto de un asterisco, el estándar dice que un día coincide si coincide cualquiera de los dos, no ambos. Por eso 0 9 13 * 5 se ejecuta a las 09:00 todos los días 13 y todos los viernes, no solo el viernes 13. En noviembre de 2026 son los días 6, 13 (que cae en viernes y se ejecuta una vez), 20 y 27.
Para combinar los dos, programa un campo y deja que la orden compruebe el otro. En un crontab, un % en la orden se convierte en salto de línea, así que hay que escribirlo \%:
0 9 13 * * [ "$(date +\%u)" = 5 ] && /usr/local/bin/jobSolo el viernes 13: se ejecuta todos los días 13 y la tarea arranca cuando el día de la semana es 5 (viernes).0 18 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /usr/local/bin/reportEl último día del mes, con GNU date (Linux): se ejecuta del 28 al 31 y la tarea arranca cuando mañana es día 1.
Zonas horarias y horario de verano
Cron lee la programación en la zona horaria de la máquina o del servicio que la ejecuta, que en servidores y plataformas alojadas suele ser UTC. 0 9 * * * en un servidor UTC se ejecuta a las 10:00 u 11:00 en Madrid, París, Berlín o Roma, según la época del año, y a las 09:00 o 10:00 en Lisboa y Londres. Brasil no tiene horario de verano, así que en São Paulo es siempre a las 06:00. cronie permite que un crontab fije su propia zona con la variable CRON_TZ; GitHub Actions admite una clave timezone junto a cron; Kubernetes tiene el campo .spec.timeZone, estable desde la versión 1.27, y rechaza CRON_TZ o TZ escritos dentro de la programación.
El horario de verano hace que algunas horas locales desaparezcan o se repitan. El 29 de marzo de 2026 los relojes de Madrid, París, Berlín y Roma pasan de las 02:00 a las 03:00, así que 30 2 * * * nombra una hora que no existe; el 25 de octubre de 2026 la hora de 02:00 a 03:00 ocurre dos veces. En Lisboa y Londres es la hora de 01:00 a 02:00.
| Programador | Tarea en la hora que se salta | Tarea en la hora repetida |
|---|---|---|
| cronie (cambios de menos de 3 horas, tareas a hora fija o menos frecuentes que cada hora) | se ejecuta justo después del cambio | se ejecuta una vez |
| GitHub Actions (con timezone) | pasa a la siguiente hora válida: 02:30 pasa a 03:00 | no se indica |
| AWS EventBridge Scheduler (con zona horaria) | se omite | se ejecuta una vez |
| Programaciones en UTC | sin efecto | sin efecto |
cronie programa con normalidad las tareas que se ejecutan más de una vez por hora, y su manual de crontab advierte en general que las horas que faltan nunca coinciden y las repetidas pueden ejecutarse dos veces, así que el comportamiento cambia de un cron a otro. Lo seguro es mantener la programación en UTC o evitar las horas entre las 01:00 y las 03:00 locales para tareas que deban ejecutarse exactamente una vez al día.
El entorno en que se ejecuta una tarea
Una tarea de cron no recibe el entorno de tu terminal. El estándar ejecuta la orden con sh y un conjunto mínimo de variables (HOME, LOGNAME, PATH y SHELL), así que una orden que funciona al escribirla puede fallar en cron porque un programa no está en el PATH o falta una variable. Usa rutas completas o define PATH al principio del crontab.
Todo lo que la orden imprime se envía por correo al dueño del crontab; cronie lo envía a la dirección de MAILTO y no envía nada si MAILTO está vacía. En un servidor sin correo configurado esa salida se pierde con facilidad, así que es mejor enviarla a un archivo de registro.
0 3 * * * /usr/local/bin/backup >> /var/log/backup.log 2>&1Añade la salida normal y los errores a un registro.5 0 * * * tar czf /srv/backup-$(date +\%F).tgz /srv/dataCrea un archivo con la fecha en el nombre: los signos % van escapados.*/5 * * * * flock -n /tmp/sync.lock /usr/local/bin/syncSe salta una ejecución mientras la anterior tenga el bloqueo.
Cron lanza cada ejecución a su hora tanto si la anterior ha terminado como si no, así que una tarea que a veces dura más que su intervalo puede acabar ejecutándose dos veces a la vez. flock, de util-linux, mantiene un archivo de bloqueo mientras dura la orden; con -n se rinde enseguida, con código de salida 1, si el bloqueo está ocupado. Kubernetes trae la misma opción: concurrencyPolicy es Allow por defecto y puede ser Forbid (omitir la nueva ejecución) o Replace (detener la anterior).
La misma programación en otras plataformas
El formato de cinco campos no es universal. Algunos programadores añaden segundos o un año, otros exigen un signo de interrogación en uno de los dos campos de día porque no admiten la regla de uno u otro, y otros numeran los días de la semana desde 1. «Los días laborables a las 09:00» en cada uno:
| Programador | Expresión | Notas |
|---|---|---|
| Cron de Linux (cronie) | 0 9 * * 1-5 | Zona horaria de la máquina, o CRON_TZ. |
| GitHub Actions | 0 9 * * 1-5 | UTC salvo que se indique timezone; como mucho una vez cada 5 minutos; sin atajos con @. |
| Kubernetes CronJob | 0 9 * * 1-5 | Zona horaria del controlador, o .spec.timeZone. |
| AWS EventBridge | cron(0 9 ? * MON-FRI *) | Seis campos con año; ? en un campo de día; los días 1–7 empiezan en domingo; las reglas programadas usan UTC. |
| Quartz (Java) | 0 0 9 ? * MON-FRI | Segundos primero, año opcional; ? en un campo de día; los días 1–7 empiezan en domingo. |
| Temporizador de systemd | OnCalendar=Mon..Fri *-*-* 09:00:00 | El día de la semana y la fecha deben coincidir los dos; *-*~01 es el último día del mes. |
Al copiar una programación entre sistemas, comprueba el número de campos y cómo se numeran los días de la semana: el 1 es lunes en cron, pero domingo en Quartz y en EventBridge.
Por qué no se ejecutó una tarea
- La máquina estaba apagada o en reposo a esa hora: cron no recupera las ejecuciones perdidas. Un temporizador de systemd con Persistent=true sí lo hace, en el siguiente arranque.
- La programación está en otra zona horaria, normalmente UTC.
- La orden depende del PATH, de una variable o de la carpeta actual de tu terminal.
- Un % sin escapar cortó la orden en ese punto.
- Se fijaron el día del mes y el día de la semana, y la regla de uno u otro la ejecutó otros días, o la tarea esperaba un día que nunca llegó.
- En una plataforma alojada, una ejecución se retrasó o se descartó: GitHub avisa de que las ejecuciones programadas pueden retrasarse en momentos de mucha carga, como el inicio de cada hora, y desactiva las programaciones de los repositorios públicos tras 60 días sin actividad.
Antes de confiar en una programación, reléela campo por campo y, en Linux, comprueba las próximas ejecuciones: systemd-analyze calendar las muestra para una expresión de temporizador.
Preguntas frecuentes
¿El domingo es 0 o 7?
Los dos, en cronie y en la mayoría de los cron de Linux. El estándar POSIX solo define del 0 al 6, con el 0 para el domingo, así que el 0 es la opción portable.
¿Puede cron ejecutar una tarea cada 30 segundos?
No: la unidad más pequeña es el minuto. Los programadores con campo de segundos, como Quartz, sí pueden; en Linux también un temporizador de systemd.
¿Cómo ejecuto una tarea cada 90 minutos?
Con dos líneas: 0 0-21/3 * * * y 30 1-22/3 * * *. Juntas se ejecutan cada hora y media, de 00:00 a 22:30.
¿Qué significa 0 0 * * 0?
Medianoche al empezar cada domingo. Equivale a @weekly en cronie.
¿Por qué mi tarea se ejecuta días que no elegí?
Probablemente porque están fijados los dos campos de día. El cron estándar ejecuta la tarea cuando coincide el día del mes o el día de la semana.
Referencias
- The Open Group Base Specifications (POSIX.1-2024) — crontab
- cronie — crontab(5): tables for driving cron
- cronie — cron(8): daylight saving time and other time changes
- util-linux — flock(1)
- GitHub Docs — Events that trigger workflows: schedule
- Kubernetes documentation — CronJob
- Amazon EventBridge — Cron expressions for scheduled rules
- Amazon EventBridge Scheduler — Schedule types (time zones and daylight saving)
- Quartz Scheduler — CronTrigger tutorial
- systemd — systemd.timer(5) and systemd.time(7): calendar events
Guías relacionadas
- Direcciones IP explicadas: IPv4, IPv6 y los rangos reservados
- Subredes y CIDR explicados: prefijos, máscaras y hosts utilizables
- Registros DNS explicados: A, AAAA, CNAME, MX, TXT y NS
- ¿Está caído un sitio web o solo te pasa a ti? Cómo comprobarlo paso a paso
- Códigos de estado HTTP y errores de Cloudflare: qué significan 403, 404, 429, 500, 503 y 521
- Marcas de tiempo Unix y zonas horarias explicadas
- Contar fechas: días entre fechas, años bisiestos y semanas ISO
- Husos horarios y horario de verano: reuniones entre ciudades
- Formatos de imagen explicados: JPEG, PNG, WebP, AVIF y HEIC
- El tamaño de una imagen explicado: píxeles, DPI, peso y calidad
- Las fotos del móvil explicadas: HEIC, metadatos EXIF y tu privacidad
- Fortaleza de las contraseñas explicada: longitud, entropía y frases de contraseña
- Funciones hash: SHA-256, sumas de comprobación y hash de contraseñas
- JSON Web Tokens: claims, firmas y validación segura
- Claves API explicadas: longitud, formato, almacenamiento y rotación
- Los UUID explicados: v4, v7 y cómo elegir la versión adecuada
- Los códigos QR explicados: capacidad, corrección de errores y tamaño de impresión
- Porcentajes e IVA explicados: añadir, quitar y encadenar
- Errores comunes en JSON y cómo corregirlos
- Los archivos CSV explicados: separadores, comillas, codificación y Excel
- Las codificaciones de texto explicadas: UTF-8, Base64 y codificación URL
- Expresiones regulares: clases, cuantificadores y grupos
- MB o MiB, Mbps o MB/s: tamaños de archivo y velocidades
- Parámetros UTM explicados: etiquetar enlaces para la analítica
- Punto de equilibrio, margen y ROAS: los números de un pequeño negocio
- Tarifas freelance explicadas: tarifa por hora, tarifa diaria y horas facturables
- Editar PDF en el navegador: qué se conserva al unir, dividir y convertir