Un cron job es una tarea que corre automáticamente según un horario; el Cloud Code programado aplica la idea a funciones serverless. El linaje va del Unix Version 7, pasando por Vixie cron (1987), hasta los programadores de plataforma de hoy — y el modelo casi no cambió: un daemon despierta cada minuto, lee una tabla de líneas horario + comando — la crontab — y dispara lo que coincida. Lo que cambió fue todo lo que rodea al modelo: dónde vive el reloj, qué pasa cuando una ejecución falla, y si alguien se entera.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| La sintaxis | Cinco campos — minuto · hora · día del mes · mes · día de la semana |
| El piso | Granularidad de un minuto; los segundos exigen otro programador |
| Los detalles traicioneros | El horario de verano salta y duplica · la regla OR entre día-del-mes y día-de-la-semana |
| La brecha de confiabilidad | Cron clásico: sin reintentos, sin catch-up, sin clúster, falla silenciosa |
| La forma moderna | La programación como config de plataforma sobre una función — reloj, logs y reintentos gestionados |
Cron en una pantalla
┌ minuto (0–59) ┌ hora (0–23) ┌ día del mes (1–31) ┌ mes ┌ día de la semana (0–7)
* * * * * comando
0 2 * * * todos los días a las 02:00 */15 * * * * cada 15 minutos
0 9 * * 1-5 días hábiles a las 09:00 0 6,18 * * * a las 06:00 y 18:00
0 0 1,15 * * los días 1 y 15 @daily = 0 0 * * *
@reboot una vez al iniciar el daemon @hourly = 0 * * * *
crontab -e edita tu tabla crontab -l la lista
crontab -ri elimina (con confirmación — el -r a secas la borra sin preguntar)
El equivalente moderno — el job en código, la programación en un dashboard, el cliente solo leyendo los resultados:
// JavaScript — Cloud Code (cloud/main.js)
// A scheduled job: defined in code, scheduled in the dashboard
Parse.Cloud.job('nightlyReport', async (request) => {
const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
// Idempotent by date key: a rerun overwrites tonight's report, not duplicates
const existing = await new Parse.Query('DailyReport')
.equalTo('runDate', dateKey(new Date()))
.first({ useMasterKey: true });
const report = existing ?? new Parse.Object('DailyReport');
report.set('runDate', dateKey(new Date()));
report.set('summary', await summarizeOrdersSince(since));
await report.save(null, { useMasterKey: true });
request.message('Report written'); // shows in the dashboard's job status
}); // Flutter / Dart — Back4app Flutter SDK
// Clients consume what the schedule produces — no crontab in sight
final query = QueryBuilder<ParseObject>(ParseObject('DailyReport'))
..orderByDescending('runDate')
..setLimit(1);
final latest = (await query.query()).results?.first as ParseObject?;
print(latest?.get<String>('summary'));
// The report exists because a scheduled Cloud Job ran at 02:00 UTC —
// defined in code, scheduled in the dashboard, logged by the platform. // iOS / Swift — Back4app Swift SDK
// Clients consume what the schedule produces — no crontab in sight
let latest = try await DailyReport.query()
.order([.descending("runDate")])
.first()
print(latest.summary ?? "")
// The report exists because a scheduled Cloud Job ran at 02:00 UTC —
// defined in code, scheduled in the dashboard, logged by the platform. // Android / Kotlin — Back4app Android SDK
// Clients consume what the schedule produces — no crontab in sight
val query = ParseQuery.getQuery<ParseObject>("DailyReport")
query.orderByDescending("runDate")
query.limit = 1
val latest = query.find().firstOrNull()
println(latest?.getString("summary"))
// The report exists because a scheduled Cloud Job ran at 02:00 UTC —
// defined in code, scheduled in the dashboard, logged by the platform. Los detalles traicioneros que la man page conoce
Cuatro semánticas de crontab(5) que sorprenden a casi todo el mundo. La regla OR: cuando tanto el día del mes como el día de la semana están restringidos, el job se dispara cuando coincide cualquiera de los dos — 0 0 13 * 5 corre todos los días 13 y todos los viernes, no solo los viernes 13. El horario de verano, al pie de la letra: los jobs programados en la “hora que desaparece” del adelanto nunca corren; los horarios que ocurren dos veces al atrasar el reloj corren dos veces — programa en UTC, mantén el trabajo crítico fuera de la madrugada local. Los pasos se reinician en el borde del campo: */90 en el campo del minuto no puede significar “cada 90 minutos” — el contador se reinicia cada hora; los intervalos irregulares de verdad exigen un programador de verdad. El piso del minuto: el daemon escanea una vez por minuto; cualquier cosa más fina es trabajo de otra herramienta.
La brecha de confiabilidad
El cron clásico es un programador, no un sistema de confiabilidad, y la brecha tiene forma: sin reintentos (una ejecución fallida es una ejecución fallida); sin catch-up (una máquina dormida a las 02:00 se salta la ejecución — anacron y la opción de persistencia de los timers de systemd existen exactamente para esto); sin historia de clúster (dos servidores con la misma crontab corren todo por duplicado; un solo servidor es un punto único de falla); falla silenciosa (la salida se envía por correo a una cuenta local que nadie lee). Los programadores modernos responden a cada brecha con política con nombre: políticas de misfire (el fire-now vs. skip de Quartz), políticas de concurrencia (el allow/forbid/replace de Kubernetes para ejecuciones superpuestas) y semántica de salto por deadline — documentando con honestidad que la programación distribuida es aproximadamente una vez: una ejecución puede, de vez en cuando, duplicarse o no dispararse. Lo que lleva a la disciplina que ambos mundos comparten: los jobs programados deben ser idempotentes — con clave por su período (el reporte de las pestañas de código, con clave por fecha), para que una reejecución sobrescriba en lugar de duplicar, y una ejecución perdida sea recuperable corriendo tarde.
Cron clásico vs. timers de systemd vs. programadores distribuidos vs. funciones programadas
| Cron clásico | Timers de systemd | Distribuido (estilo Quartz/K8s) | Funciones programadas en la nube | |
|---|---|---|---|---|
| Vive en | La crontab de una máquina | Una máquina, unit files | Un clúster | Config de plataforma sobre una función |
| Catch-up | Ninguno (anacron lo parcha) | Persistent=true | Políticas de misfire | Política de la plataforma |
| Superposición | Arranca otra copia | Serializado por unit | allow / forbid / replace | Política de la plataforma |
| Reintentos | Ninguno | Reglas de restart del servicio | Configurable | Incluido |
| Visibilidad de fallas | Correo local, sin leer | journald | Objetos de estado del job | Estado + logs en el dashboard |
| Servidor que mantener vivo | Sí — y es el SPOF | Sí | El clúster | No |
Vigilar las programaciones
La falla programada es la falla más silenciosa de la computación: un job que nunca se disparó no emite nada — ni excepción, ni línea de log, ni correo — porque nada corrió. El patrón que lo arregla invierte la alarma: monitoreo por heartbeat — el job hace ping a una URL al completarse con éxito, el monitor espera el ping dentro de un período de gracia, y la ausencia dispara la alerta. Combínalo con la higiene mundana que el cron clásico nunca tuvo: la duración de las ejecuciones seguida en el tiempo (el reporte que tomaba 4 minutos el mes pasado y 40 esta noche te está diciendo algo), la salida capturada en logs de verdad en lugar de correo local, y el inventario de programaciones documentado — porque una crontab repartida entre cinco servidores es como las organizaciones descubren jobs que olvidaron que corrían.
Casos de uso comunes
- Reportes y resúmenes — el rollup nocturno que las pestañas de código esbozan: agregar, escribir, notificar.
- Limpieza y expiración — barridos de TTL, poda de huérfanos, pasadas de expiración de sesiones y tokens.
- Sincronización de datos — pulls periódicos desde sistemas de terceros que no ofrecen webhooks.
- Recordatorios y reenganche — envíos basados en tiempo: fines de trial, carritos abandonados, avisos de renovación.
- Salud y reconciliación — la auditoría programada que atrapa lo que los caminos orientados a eventos pasaron por alto.
¿Qué programador deberías usar? Matriz de decisión
| Situación | Usa |
|---|---|
| Una máquina Linux que ya operas | Cron clásico — con locks y un heartbeat |
| Máquinas intermitentes o tipo laptop | anacron / timers de systemd con persistencia |
| Un clúster que ya corres | Su controlador nativo de jobs programados |
| Backend de app en un BaaS | Cloud Jobs programados — sin servidor, logs incluidos |
| Intervalos por debajo del minuto o irregulares | Un programador o cola de verdad, no aritmética de crontab |
| Trabajo de eventos mal etiquetado como programado | La cola de jobs — cron solo la alimenta |
Limitaciones y trade-offs
- Los disparadores de tiempo dicen cuándo, no si. Una programación se dispara haya trabajo o no; los triggers orientados a eventos y las colas sirven al trabajo que llega de forma irregular.
- Aproximadamente-una-vez es el contrato honesto. Incluso los programadores gestionados de vez en cuando duplican o se saltan; la idempotencia es responsabilidad del job en todas partes.
- La zona horaria es una decisión de política. Las programaciones en UTC sobreviven al horario de verano; las programaciones en hora local sirven a humanos — elige a propósito, documenta en voz alta.
- La frecuencia tiene piso y precio. Nivel de minuto en el cron clásico, pisos dependientes de plataforma en el resto; el polling vía cron a alta frecuencia suele ser una queue disfrazada de reloj.
- Las programaciones se acumulan en silencio. Todo job nocturno “temporal” es permanente hasta que se inventaría; el dashboard que las lista todas es una función subestimada.
Cloud Code Programado en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. Programar aquí es la columna moderna de la tabla comparativa hecha concreta: define Parse.Cloud.job en Cloud Code — el reporte nocturno de las pestañas de código, idempotente por clave de fecha, acceso con master key para la agregación entre usuarios — y luego prográmalo en el panel de Background Jobs del dashboard: nombre del job, hora de inicio, recurrencia, sin sintaxis de crontab y sin servidor en cuya crontab viviría. Las ejecuciones, el estado y los errores caen en los logs del servidor y en el panel de jobs — la columna de visibilidad de fallas respondida por defecto — y las disciplinas con las que cierra este artículo siguen siendo tuyas por diseño: dale a cada job una clave por período, mantenlo idempotente, y deja que un heartbeat confirme que el reloj de la plataforma y tu lógica estuvieron de acuerdo esta noche, como todas las noches.
Preguntas frecuentes
¿Qué es un cron job?
Una tarea programada para correr automáticamente en horarios o intervalos fijos — clásicamente por el daemon cron en sistemas Unix leyendo una crontab y, por extensión, cualquier job disparado por tiempo en cualquier plataforma. El nombre viene de chronos, "tiempo" en griego.
¿Qué significan los cinco campos de una expresión cron?
Minuto (0–59), hora (0–23), día del mes (1–31), mes (1–12) y día de la semana (0–7, donde tanto 0 como 7 son domingo), seguidos del comando. El asterisco significa todos los valores; las comas listan, los guiones definen rangos, las barras definen pasos.
¿Qué pasa si la máquina está apagada a la hora programada?
El cron clásico simplemente se salta la ejecución — no hay recuperación. Por eso existe anacron para máquinas encendidas de forma intermitente, por eso los timers de systemd ofrecen una opción de persistencia, y por eso los programadores modernos tratan la política de catch-up como configuración explícita y no como sorpresa.
¿Cómo maneja cron el horario de verano?
Mal, por defecto: según el propio manual, los jobs programados en la "hora que desaparece" del adelanto nunca corren, y los horarios que ocurren dos veces al atrasar el reloj corren dos veces. El consejo de siempre: programa en UTC y mantén los jobs críticos fuera de la ventana local de 00:00–03:00.
¿Y si un job sigue corriendo cuando empieza la siguiente ejecución?
El cron clásico arranca alegremente una segunda copia — y una tercera. La superposición exige una respuesta explícita: un archivo de lock que hace que la ejecución tardía se salte el turno, o las políticas formalizadas de los programadores modernos — permitir, prohibir o reemplazar la instancia en ejecución.
¿Cómo sé cuándo falla un cron job?
Por defecto, no lo sabes — la salida va a un buzón de correo local que nadie lee, y una programación que nunca se disparó no produce error alguno, porque nada corrió. El patrón que lo arregla: monitoreo por heartbeat — el job hace ping a una URL al tener éxito, y la ausencia del ping es lo que levanta la alarma.
¿Puede cron correr un job más de una vez por minuto?
No — un minuto es el piso del cron clásico; el daemon despierta, escanea y dispara por minuto. La granularidad de segundos exige otro programador: expresiones de seis campos al estilo Quartz, un loop dentro de un servicio, o una cola de eventos en lugar de un reloj.
¿Necesito un servidor para correr cron jobs?
Ya no. Las plataformas serverless atan la programación directamente a una función: la plataforma es dueña del reloj, dispara el job, registra logs y estado y reintenta según la política — la programación como configuración, no como un archivo en una máquina que debes mantener viva.