Automatización Linux, scripting Bash y herramientas para sysadmins

Systemd timers: lo que cron nunca pudo hacer

#linux#systemd#automation#cron

El problema con cron

Usé cron por años. Funciona. Pero tiene tres problemas que me cansaron:

  1. Si la máquina está apagada a la hora del cronjob, se pierde. No hay reintento.
  2. Los logs dependen de que tú redirijas la salida. Si no lo hiciste, no sabes si falló.
  3. No hay forma nativa de decir “ejecuta esto solo si el servicio X está corriendo”.

systemd timers resuelve los tres. Y varios más.

Lo básico

Un timer son dos archivos: el timer (cuándo) y el servicio (qué). El servicio es un unit común de systemd. El timer le dice cuándo dispararlo.

# /etc/systemd/system/limpieza.timer
[Unit]
Description=Limpieza semanal de temporales

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target

Persistent=true es lo que cron no tiene: si el server estuvo apagado el domingo a las 3 AM, el timer se dispara apenas bootea. No se pierde la ejecución.

Calendarios que no duelen

La sintaxis de OnCalendar= es más legible que los 5 campos de cron:

# Todos los días 03:00
OnCalendar=daily

# Lunes y viernes 08:30
OnCalendar=Mon,Fri *-*-* 08:30:00

# Cada 15 minutos
OnCalendar=*:0/15

# Primer domingo del mes, 02:00
OnCalendar=Sun *-*-1..7 02:00:00

Para validar una expresión sin desplegar nada:

systemd-analyze calendar "Mon,Fri *-*-* 08:30:00" --iterations=5

Te escupe las próximas 5 fechas. Útil para no despertar a las 3 AM un sábado por error de tipeo.

Monotónicos: cuando no importa la hora

A veces no necesitas un calendario. Necesitas “10 minutos después de bootear” o “cada 6 horas desde la última ejecución”. Para eso están los timers monotónicos:

DirectivaSe dispara…
OnBootSec=X tiempo después del boot
OnActiveSec=X tiempo después de activar el timer
OnUnitActiveSec=X tiempo después de la última ejecución
OnUnitInactiveSec=X tiempo después de que terminó la última ejecución

Caso real: limpiar /tmp 10 minutos después del boot, y luego cada 6 horas:

[Timer]
OnBootSec=10min
OnUnitActiveSec=6h

Sin cron, sin scripts wrapper, sin sleep. Dos líneas.

Aleatorización para no tumbarte el NAS

Si administras 50 servidores y todos hacen backup a las 3 AM contra el mismo NAS, lo saturas. Con cron la solución era sleep $((RANDOM % 1800)) && backup.sh. Con systemd:

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800

El timer calcula un retardo aleatorio entre 0 y 1800 segundos. Lo mantiene constante hasta que reinicie systemd. Tus 50 backups se distribuyen solos en una ventana de 30 minutos.

Dependencias: que no se dispare si no hay internet

El timer puede exigir que ciertos targets o servicios estén activos:

[Unit]
Description=Backup diario a la nube
After=network-online.target
Wants=network-online.target

[Timer]
OnCalendar=daily

Y el servicio asociado puede ser más granular. Si tu backup necesita PostgreSQL corriendo:

# En backup.service (no en el .timer)
[Unit]
After=postgresql.service
Requires=postgresql.service

Si PostgreSQL no está activo, el timer se dispara igual, pero el servicio falla. Y systemd lo registra. No hay silencio cómplice como en cron.

Debugging sin adivinar

¿Cuándo se dispara la próxima vez?

systemctl list-timers --all | grep backup

Devuelve una tabla con NEXT, LEFT, LAST, PASSED. Sin parsear logs ni revisar crontabs.

¿Falló la última ejecución?

systemctl show backup.service -p ExecMainStatus

Si no es 0, algo pasó. Para ver qué:

journalctl -u backup.service --since "1 day ago"

Forzar ejecución manual

systemctl start backup.service

Esto ejecuta el servicio ya. No toca el timer. No reinicia el ciclo. Ideal para tests.

Cron vs systemd timer

Funcionalidadcronsystemd timer
Calendarios
Eventos relativos (boot, última ejecución)No
Persistencia tras bootSe pierdePersistent=true
AleatorizaciónManualNativa
Dependencias de serviciosNoAfter=, Requires=
LogsManual (redirigir salida)journalctl automático
Seguridad (aislamiento)NoPrivateTmp=, ProtectSystem=, etc
DebuggingBuscar en logssystemctl list-timers, journalctl

Una verificación rápida: ¿tu sistema ya migró los cronjobs a timers?

ls /usr/lib/systemd/system/*.timer

En Ubuntu 22.04 aparecen logrotate.timer, man-db.timer, apt-daily.timer, fstrim.timer. Los propios maintainers de la distro ya hicieron el cambio.


Si tu sistema corre systemd (Ubuntu 16.04+, Debian 8+, RHEL 7+), no hay razón técnica para seguir escribiendo cronjobs nuevos. Los que ya tienes funcionando déjalos. Pero lo nuevo, en timer.