Caja systemd propia → timer. Jobs de una línea, contenedores, cajas que no montaste tú → cron.
Cron falla en silencio — manda un correo a un buzón local que nadie lee. Los timers loguean cada ejecución al journal: journalctl -u mytimer.service.
Persistent=true re-ejecuta los trabajos perdidos mientras la máquina estaba apagada ; cron los salta en silencio.
El mismo job — respaldo nocturno a las 03:30 — en ambos relojes, y lo que cada uno deja atrás:
CRON · una línea
30 3 * * * /usr/local/bin/backup.sh
se ejecuta cuando el reloj de pared coincide. Si falla, el output se va en correo a un buzón local — normalmente sin leer. ¿La máquina estaba apagada a las 03:30? Saltado, en silencio.
¿Llegó a dispararse? journalctl -u cron -n 20 o grep CRON /var/log/syslog
TIMER DE SYSTEMD · dos ficheros de unidad
# foo.timer
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
# foo.service — the job itself
una unidad de primera clase: estado, dependencias, logs por ejecución en el journal. ¿La máquina dormía a la hora? Persistent=true lo corre una vez al arrancar.
compruébalo: systemd-analyze calendar "*-*-* 03:30:00" — el próximo disparo, antes de guardar
el veredicto del horario: los cinco campos de cron son el incumbent compacto; OnCalendar es más largo pero estrictamente más expresivo — Mon..Fri 09..17:00:00 es una línea, y RandomizedDelaySec evita mil máquinas machacando un solo servidor
estos relojes re-estampan desde los propios comandos del expediente — no pueden leer el journal de tu máquina; dónde está realmente tu journal: secciones 04 y 05.
Persistent=true se corre una vez al boot si le saltó su horario; cron simplemente salta.network-online.target o puntos de montaje; cron te obliga a escribir a mano la lógica de reintento dentro del script.OnBootSec=15min — cambios del reloj de pared no pueden confundirlo.Tres comandos responden todo — lista qué está planificado y cuándo disparará la próxima vez, corre el job a mano tal como lo haría el timer, y después lee sus logs:
systemctl list-timers --all # every timer, next + last run
sudo systemctl start backup.service # fire the job now, same unit as the timer
journalctl -u backup.service -f # watch its output live
Fíjate en el reparto: systemctl start backup.timer arma el horario; el service es el job. Si list-timers muestra tu timer, systemctl status backup.service está en verde, y el journal muestra tu output, toda la cadena funciona.
verify: systemctl list-timers --all → tu timer, última y próxima ejecución listadas
Los jobs cron corren con un entorno despojado, por eso «funciona en mi shell, falla en cron» es un género de bug. Para reproducir cron fielmente, corre el comando a través de sh con el entorno que usaría cron:
crontab -l # confirm the exact line
env -i SHELL=/bin/sh PATH=/usr/bin:/bin sh -c '/usr/local/bin/backup.sh'
grep CRON /var/log/syslog | tail # did cron even fire it? (Debian/Ubuntu)
journalctl -u cron -n 20 # same, on systemd distros
Esa env -i es la prueba manual honesta: un entorno desnudo con el PATH por defecto de cron. Los dos clásicos de cron — un PATH desnudo y un % sin comillas (cron lo trata como un salto de línea) — aparecen de inmediato en esa prueba.
verify: journalctl -u cron -n 20 → la entrada CRON de tu comando
Un timer que parece muerto suele tener su service fallando al instante. Cuatro causas, en el orden para comprobarlas:
foo.timer dispara foo.service — una errata o una unidad renombrada y el timer «corre» al vacío. systemctl cat foo.timer muestra exactamente a quién apunta.sudo systemctl daemon-reload && sudo systemctl restart foo.timer. Sin esto tu nuevo horario no está vivo.systemctl status foo.timer siempre muestra active incluso estando ocioso — mira mejor list-timers en su lugar.journalctl -u foo.service --since -1h muestra el crash que list-timers esconde.verify: journalctl -u foo.service --since -1h → el crash que list-timers esconde
| cron | timer systemd | |
|---|---|---|
| Instalación | una línea de crontab | dos ficheros de unidad |
| Logging | correo local, normalmente sin leer | journal, por unidad |
| Ejecución perdida (máquina apagada) | saltada | se corre una vez con Persistent=true |
| Dependencias / orden | ninguna — DIY en script | dependencias de unidad completas |
| Retardo aleatorizado | DIY con $RANDOM | RandomizedDelaySec= |
| Prueba / dry-run del horario | no | systemd-analyze calendar |
| Existe en todas partes | sí — contenedores, BSD, embebidos | necesita systemd (PID 1) |
| Jobs de usuario sin root | crontab -e | systemd --user unidades |
Reglas del pulgar : llevar un job a tu propia máquina systemd → timer. Recordatorio personal rápido o una caja que no montaste → cron. Cualquier cosa con dependencias, reintentos, o la necesidad de saber si de verdad corrió → timer, siempre. El patrón clásico — un rsync nocturno de respaldo bajo un timer con Persistent=true — garantiza que el respaldo ocurre incluso si la máquina dormía en el minuto pautado, cosa que cron simplemente no puede ofrecer.
fin de los dos relojes · nada se descargó · todo es local