Saltar al contenido principal

Sintaxis de expresiones cron

Una expresión cron son cinco campos — minuto, hora, día del mes, mes, día de la semana —, cada uno con *, un número, un rango como 1-5, un paso como */15 o una lista como 1,3,5. Todos los campos se intersecan, salvo por la trampa clásica: si restringes a la vez el día del mes y el día de la semana, cron los combina con OR, no con AND. El horario sigue el reloj del servidor.

Solo local
¿Solo quieres la herramienta? Abre Expresión cronLee una programación y mira cuándo se ejecuta de verdadAbrir herramienta

Sintaxis de expresiones cron: explicación

Cron es el planificador de Unix desde los años setenta, y su gramática apenas se ha movido: cinco campos pequeños que en conjunto responden a «cuándo se ejecuta esto». Te vas a topar con la sintaxis en crontabs, sistemas de CI, Kubernetes CronJobs, planes de mantenimiento de bases de datos y la mitad de los planificadores de internet — una sintaxis lo bastante mayor para votar, que sigue ganando porque cabe en una línea.

La gramática es simple; los bordes, no. Todos los campos se intersecan con los demás menos uno, y esa única excepción — día del mes frente a día de la semana — hace lo contrario de lo que la intuición exige. El domingo responde tanto al 0 como al 7. Los amables atajos @daily son extensiones, no parte del estándar. Y no hay ningún campo de zona horaria en la sintaxis, así que cada expresión hereda en silencio el reloj que use su host.

Esta guía recorre los cinco campos, los operadores, la trampa de los campos de día, los atajos y la realidad de las zonas horarias, y cierra con cómo probar una expresión antes de apostar a ella una tarea de producción. El probador enlazado aquí se ejecuta por completo en tu navegador — la expresión nunca sale de la pestaña.

La manera más rápida de que estas reglas se te queden es romperlas a propósito y ver cómo reacciona un parser, así que deja el probador de expresiones cron abierto en la pestaña de al lado: describe lo que significa tu expresión y lista las próximas horas de ejecución, calculadas localmente.

Los horarios modernos suelen vivir en archivos de despliegue más que en crontabs, así que cuando tu expresión viaja dentro de una definición de pipeline, la guía de conceptos básicos de YAML cubre las reglas de indentación, comillas y tipos que obedece ese archivo.

Los cinco campos

Una expresión cron estándar son exactamente cinco campos separados por espacios, en un orden fijo: minuto, hora, día del mes, mes y día de la semana. Una tarea se dispara cuando cada campo coincide con el momento actual — los campos se combinan con AND. Escribe 30 4 * * * y la tarea se ejecuta a las 04:30 todos los días; todos los campos deben coincidir para que el momento cuente.

Cada campo acepta números dentro de su rango, y los campos de mes y de día de la semana también aceptan nombres — JAN-DEC y SUN-SAT —, que algunos planificadores aceptan sin distinguir mayúsculas de minúsculas. Los rangos merecen memorizarse porque dos de ellos empiezan en un sitio inesperado: los meses cuentan desde 1, y los días de la semana desde 0. Todo lo demás de la sintaxis de cron son operadores puestos sobre estos campos.

CampoPosiciónValores permitidosNombres aceptados
Minuto1.º0-59—
Hora2.º0-23—
Día del mes3.º1-31—
Mes4.º1-12JAN-DEC
Día de la semana5.º0-7 (0 y 7 son domingo)SUN-SAT

Rangos, pasos y listas

Tres operadores hacen la mayor parte del trabajo en las expresiones reales. Un rango, 1-5, coincide con cada valor entre sus extremos, ambos incluidos. Un paso, escrito tras una barra, coincide con cada enésimo valor del campo o del rango que lo precede — */15 en el campo de minutos significa 0, 15, 30, 45, y 9-17/2 en el campo de horas significa una hora sí y otra no desde las 9 hasta las 17. Una lista, 1,3,5, coincide exactamente con los valores nombrados, y los operadores se combinan: 1-5,10 y 0-30/5 son ambos legales.

Dos hábitos mantienen honestos los pasos. Primero, */N siempre incluye el mínimo del campo — así que */15 toca el minuto 0, y por eso las flotas de tareas */15 despiertan todas juntas al comienzo de cada hora. Segundo, un paso sobre un rango explícito suele ser más claro que una lista: 0-59/10 se lee como «cada diez minutos» de una forma en que 0,10,20,30,40,50 no lo consigue.

ExpresiónSe lee como
0 9 * * 1-509:00, de lunes a viernes
*/15 * * * *Cada 15 minutos
0 0 1 * *Medianoche del día 1 de cada mes
30 4 1,15 * SUN04:30 del día 1, del 15 y de cada domingo
0 22 * * 1-522:00 entre semana

Una expresión, probada

30 9 * * 1-5

Lo que dice el probador

At 09:30, on Monday through Friday.
Next runs (UTC):
  Mon 2026-09-21 09:30
  Tue 2026-09-22 09:30

La trampa del día del mes frente al día de la semana

Todos los pares de campos se intersecan — excepto el día del mes y el día de la semana, que cron combina con OR siempre que ambos estén restringidos. La sorpresa clásica: 0 0 13 * 5 no significa «viernes 13». Significa medianoche del día 13 del mes, o medianoche de cualquier viernes — una tarea que se ejecuta dos veces algunas semanas y en fechas que nunca pretendiste. Si cualquiera de los dos campos de día es *, vuelve el comportamiento AND normal y solo importa el otro.

La regla del OR no es un bug; está escrita en la documentación de POSIX y de Vixie cron, y sobrevive porque deja que los dos campos de día sirvan a amos distintos — una tarea mensual del día 1 puede coexistir con una semanal del domingo en el mismo par de campos. Pero vence a la intuición, así que los hábitos defensivos son: nunca restrinjas ambos sin haber contado los OR, y cuando de verdad necesites «viernes 13», programa a diario y comprueba la fecha en el propio comando.

El domingo es 0 y 7, y los atajos son extensiones

El día de la semana acepta tanto 0 como 7 para el domingo en Vixie cron, la implementación de la que descienden la mayoría de los sistemas, y el 7 se convierte en 0 al entrar. Es una cortesía para los rangos — SUN-SAT puede escribirse 0-6 o 1-7 —, pero también es una costura de portabilidad: otras implementaciones rechazan el 7 de plano, y un par de peculiaridades del parseo hacen que el 7 sea arriesgado en las listas. Escribir 0 para el domingo es la opción portable.

Los atajos con @ — @reboot, @yearly, @annually, @monthly, @weekly, @daily, @midnight y @hourly — son igualmente extensiones de Vixie, no parte de la gramática POSIX. Aun así, están soportados casi dondequiera que se declara compatibilidad con cron, lo que los hace válidos en la práctica, y sigue valiendo la pena expandirlos mentalmente: @daily es 0 0 * * *, medianoche de todos los días, y @reboot significa una vez cuando arranca el demonio, lo que puede ser antes o después de lo que esperas tras una reprogramación.

AtajoSe expande aSignificado
@reboot—Una vez, cuando arranca el demonio de cron
@yearly / @annually0 0 1 1 *Medianoche del 1 de enero
@monthly0 0 1 * *Medianoche del día 1 de cada mes
@weekly0 0 * * 0Medianoche de cada domingo
@daily / @midnight0 0 * * *Medianoche de todos los días
@hourly0 * * * *Al comienzo de cada hora

El campo de zona horaria que no existe

Cron no tiene campo de zona horaria. Una expresión se evalúa contra el reloj del demonio — la hora local del sistema del servidor —, así que el mismo crontab significa horas distintas en máquinas de zonas distintas, y mover una tarea de un host de Tokio a uno de Fráncfort desplaza su horario en silencio. Los contenedores lo agudizan: la mayoría se ejecutan en UTC sin importar dónde estén quienes los operan, así que «cuándo se ejecuta» y «cuándo yo lo quería» se separan por un desfase que nadie anunció.

El horario de verano añade sus propios peligros en cualquier zona de hora local: una tarea de las 02:30 no existe la noche en que los relojes se adelantan, y una de las 01:30 se ejecuta dos veces la noche en que se atrasan. Algunas implementaciones aceptan parches no portables — una línea TZ o CRON_TZ en el crontab, o una zona horaria en la configuración propia del planificador —, pero el soporte varía y la portabilidad termina donde empiezan esas líneas. La práctica honesta es saber con qué reloj se evalúa tu expresión; el probador de este sitio calcula todo en UTC y lo dice junto a cada resultado.

Probar una expresión antes de ponerla en producción

Algunas expresiones son legales e insatisfacibles — 0 0 31 2 * pide el 31 de febrero, y ningún bucle de búsqueda ingenuo sobre fechas lo encuentra jamás; por eso un probador serio acota su búsqueda por años e informa de un horario que nunca se dispara en lugar de quedarse colgado. Otras expresiones son legales y simplemente no significan lo que tú querías; la trampa del 13 o del viernes produce horarios que parecen correctos durante días antes de sorprenderte.

El flujo que caza ambas cosas: escribe la expresión, haz que te la describan con palabras y mira las próximas horas de ejecución concretas. Una descripción dice qué cree el parser que escribiste; las marcas de tiempo concretas muestran dónde se disparará de verdad. El probador de este sitio hace ambas — sintaxis estándar de cinco campos más las variantes de seis y siete campos al estilo Quartz — por completo en tu navegador, así que una expresión que contenga nombres internos de tareas o de hosts nunca sale de la pestaña.

Preguntas frecuentes

¿El probador de cron sube mis expresiones o los nombres de mis tareas?

No. El parseo, la descripción y el cálculo de las próximas ejecuciones ocurren todo en JavaScript dentro de la pestaña de tu navegador; la expresión nunca sale de tu máquina. Compruébalo en DevTools: abre el panel Network, evalúa una expresión y mira cómo la lista de peticiones se queda vacía. La Content-Security-Policy del sitio también prohíbe las conexiones salientes de los scripts de la página.

¿Por qué 0 0 13 * 5 no significa viernes 13?

Porque cron combina con OR los campos de día cuando ambos están restringidos: este horario se dispara a medianoche del día 13 del mes o de cualquier viernes, lo que llegue antes — no el viernes 13. Es comportamiento definido, tanto en POSIX como en Vixie cron. Para cazar la coincidencia real, programa la tarea a diario y comprueba la fecha en el comando que ejecuta.

¿Es 7 un valor válido para el día de la semana?

En Vixie cron y sus descendientes, sí — tanto 0 como 7 significan domingo, con el 7 convertido en 0 al entrar. Pero es el rincón menos portable de la sintaxis: algunas implementaciones rechazan el 7 de plano, y allí se comporta de forma extraña en listas y rangos. Escribir 0 para el domingo no cuesta nada y evita el desacuerdo por completo.

¿En qué zona horaria se ejecuta una expresión cron?

El reloj que use el demonio — la hora local del sistema del servidor, ya que cron no tiene campo de zona horaria. Los contenedores suelen significar UTC. Algunas implementaciones aceptan una línea TZ o CRON_TZ en el crontab, pero eso no es portable, y las zonas de hora local añaden peculiaridades del horario de verano en las que las tareas de las 02:30 se saltan una noche y las de las 01:30 se ejecutan dos veces. Conoce el reloj antes de fiarte del horario.

¿Son @daily, @weekly y @reboot cron estándar?

Son extensiones de Vixie cron más que POSIX, pero el soporte es casi universal en todo lo que se dice cron. Merece saber a qué se expanden: @daily es 0 0 * * *, @weekly es 0 0 * * 0, y @reboot se dispara cuando arranca el demonio — lo cual, tras una reprogramación de la máquina o un reinicio de un contenedor, es cuando eso ocurra, no una hora fija.

¿Por qué mi tarea no se ejecuta el último día del mes?

Cron no tiene sintaxis de «último día» — el día del mes llega hasta 31, y 31 solo coincide en siete meses del año. La solución estándar es una pequeña prueba de shell: programa a medianoche cada día y ejecuta solo si mañana es día 1. Los planificadores con calendarios más ricos (los temporizadores de systemd, Quartz con su flag L) lo expresan de forma nativa; POSIX cron deliberadamente no.