Los systemd timers son el reemplazo moderno de cron en sistemas Linux. Te ofrecen todo lo que hace cron — programación recurrente y de ejecución única — además de registro integrado, gestión de dependencias, control de recursos y la capacidad de recuperar ejecuciones perdidas. Si administras servidores Linux, migrar de cron a systemd timers significa menos fallos silenciosos y mucha mayor visibilidad sobre lo que hacen tus tareas programadas.

Esta guía te lleva paso a paso por la creación de timers desde cero, la conversión de trabajos de cron existentes y el uso de funcionalidades avanzadas como retrasos aleatorios y programación persistente.

Requisitos Previos

  • Un sistema Linux con systemd (Ubuntu 16.04+, CentOS 7+, Debian 8+ o cualquier distribución moderna)
  • Acceso root o sudo
  • Familiaridad básica con systemctl y archivos de unidad

Cómo Funcionan los systemd Timers

Un systemd timer se compone de dos archivos de unidad:

  1. Una unidad .service — define qué ejecutar (el comando o script real)
  2. Una unidad .timer — define cuándo ejecutarlo (la programación)

La unidad del timer activa su unidad de servicio correspondiente según la programación. Ambos archivos deben compartir el mismo nombre base (por ejemplo, backup.service y backup.timer), a menos que especifiques explícitamente una unidad diferente con Unit=.

Esta separación es una ventaja clave sobre cron: la unidad de servicio puede probarse de forma independiente, reiniciarse en caso de fallo e inspeccionarse con las herramientas estándar de systemd.

Creación de Tu Primer Timer

Vamos a crear un timer que ejecute un script de limpieza todos los días a las 2:00 AM.

Paso 1: Crear la Unidad de Servicio

sudo nano /etc/systemd/system/daily-cleanup.service
[Unit]
Description=Daily temporary file cleanup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup.sh

El Type=oneshot indica a systemd que este proceso se ejecuta hasta completarse y luego termina — no es un daemon de ejecución continua. Este es el tipo correcto para tareas programadas.

Crea el script al que hace referencia:

sudo tee /usr/local/bin/cleanup.sh > /dev/null << 'EOF'
#!/bin/bash
find /tmp -type f -mtime +7 -delete
find /var/log -name "*.gz" -mtime +30 -delete
echo "Cleanup completed at $(date)"
EOF
sudo chmod +x /usr/local/bin/cleanup.sh

Paso 2: Crear la Unidad del Timer

sudo nano /etc/systemd/system/daily-cleanup.timer
[Unit]
Description=Run daily cleanup at 2:00 AM

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Directivas clave:

DirectivaPropósito
OnCalendarProgramación basada en calendario (similar a cron)
Persistent=trueEjecutar inmediatamente si se perdió una ejecución programada (por ejemplo, si el sistema estaba apagado)
WantedBy=timers.targetNecesario para que funcione systemctl enable

Paso 3: Habilitar e Iniciar

sudo systemctl daemon-reload
sudo systemctl enable --now daily-cleanup.timer

Paso 4: Verificar

systemctl list-timers daily-cleanup.timer

Salida:

NEXT                        LEFT          LAST PASSED UNIT                  ACTIVATES
Wed 2025-12-24 02:00:00 UTC 10h left      n/a  n/a   daily-cleanup.timer   daily-cleanup.service

Prueba el servicio manualmente para confirmar que funciona:

sudo systemctl start daily-cleanup.service
journalctl -u daily-cleanup.service --no-pager -n 20

Sintaxis de OnCalendar

La directiva OnCalendar utiliza un formato flexible de expresión de calendario:

DíaDeLaSemana Año-Mes-Día Hora:Minuto:Segundo

Estos son los patrones más comunes:

ProgramaciónExpresión OnCalendar
Todos los días a medianoche*-*-* 00:00:00
Cada lunes a las 9 AMMon *-*-* 09:00:00
Primer día de cada mes*-*-01 00:00:00
Cada 15 minutos*-*-* *:00/15:00
Días laborables a las 6 PMMon..Fri *-*-* 18:00:00
Cada 1 de enero y julio*-01,07-01 00:00:00
Cada 2 horas*-*-* 00/2:00:00

Puedes validar las expresiones con systemd-analyze calendar:

systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
  Original form: Mon..Fri *-*-* 09:00:00
Normalized form: Mon..Fri *-*-* 09:00:00
    Next elapse: Mon 2025-12-29 09:00:00 UTC
       (in UTC): Mon 2025-12-29 09:00:00 UTC
       From now: 5 days left

Esta es una de las mayores ventajas sobre cron — puedes verificar tu expresión de programación antes de desplegarla.

Timers Monotónicos: Intervalos Después de Eventos

No todas las programaciones se basan en el reloj. Los timers monotónicos se activan en relación con un evento:

[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
DirectivaSe activa
OnBootSecX tiempo después del arranque
OnStartupSecX tiempo después de que inicie systemd
OnUnitActiveSecX tiempo después de la última activación del timer
OnUnitInactiveSecX tiempo después de que el servicio terminó por última vez

Esto es útil para verificaciones de salud, recopilación de métricas o cualquier tarea que deba ejecutarse a intervalos independientemente de la hora del reloj.

Ejemplo — ejecutar una verificación de salud cada 5 minutos, comenzando 1 minuto después del arranque:

[Unit]
Description=Periodic health check

[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
AccuracySec=10s

[Install]
WantedBy=timers.target

El AccuracySec=10s reduce la ventana de agrupación predeterminada de 1 minuto, de modo que tu timer se dispare más cerca de exactamente cada 5 minutos.

Funcionalidades Avanzadas

Retraso Aleatorio

Evita que múltiples servidores accedan al mismo recurso simultáneamente:

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

Esto programa la tarea entre las 3:00 AM y las 3:15 AM, con systemd seleccionando un desplazamiento aleatorio en cada ciclo de arranque.

Control de Recursos

Dado que la tarea se ejecuta como un servicio de systemd, puedes aplicar límites de recursos:

[Service]
Type=oneshot
ExecStart=/usr/local/bin/heavy-report.sh
CPUQuota=50%
MemoryMax=512M
IOWeight=50
Nice=15

Cron no tiene equivalente — tendrías que añadir llamadas a nice, ionice y ulimit dentro de cada script.

Notificación de Fallos

Activa una alerta cuando una tarea programada falla:

[Unit]
Description=Database backup
OnFailure=notify-admin@%n.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/db-backup.sh

Crea la plantilla de notificación en /etc/systemd/system/notify-admin@.service:

[Unit]
Description=Send failure alert for %i

[Service]
Type=oneshot
ExecStart=/usr/local/bin/send-alert.sh "Unit %i failed on %H"

Conversión de Trabajos de Cron a systemd Timers

Aquí tienes un enfoque sistemático para migrar trabajos de cron existentes.

Ejemplo de entrada de cron:

30 2 * * 1-5 /usr/local/bin/db-backup.sh

Esto ejecuta el respaldo a las 2:30 AM en días laborables. Las unidades equivalentes de systemd:

# /etc/systemd/system/db-backup.service
[Unit]
Description=Database backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/db-backup.sh
# /etc/systemd/system/db-backup.timer
[Unit]
Description=Database backup on weekday nights

[Timer]
OnCalendar=Mon..Fri *-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target

Referencia rápida de cron a OnCalendar:

CronOnCalendar
0 * * * * (cada hora)*-*-* *:00:00
*/5 * * * * (cada 5 min)*-*-* *:00/5:00
0 0 * * 0 (semanal dom)Sun *-*-* 00:00:00
0 0 1 * * (mensual)*-*-01 00:00:00
@rebootUsar OnBootSec=0

Después de crear las unidades:

sudo systemctl daemon-reload
sudo systemctl enable --now db-backup.timer

# Verificar
systemctl list-timers db-backup.timer

# Comentar la entrada antigua de cron
sudo crontab -e

Monitorización y Depuración de Timers

Listar Todos los Timers Activos

systemctl list-timers --all

Consultar los Logs del Timer

journalctl -u daily-cleanup.service --since today
journalctl -u daily-cleanup.service --since "2025-12-20" --until "2025-12-23"

Depurar un Timer que No se Activa

# Verificar el estado del timer
systemctl status daily-cleanup.timer

# Verificar la expresión de calendario
systemd-analyze calendar "*-*-* 02:00:00"

# Buscar errores en los archivos de unidad
systemd-analyze verify /etc/systemd/system/daily-cleanup.*

# Ver todos los timers, incluyendo los inactivos
systemctl list-timers --all --no-pager

Timers Transitorios para Tareas Puntuales

¿Necesitas ejecutar algo una sola vez en 30 minutos sin crear archivos de unidad?

systemd-run --on-active=30min /usr/local/bin/one-time-task.sh

O a una hora específica:

systemd-run --on-calendar="2025-12-24 15:00:00" /usr/local/bin/holiday-report.sh

Solución de Problemas

El timer muestra “n/a” en NEXT: El timer está cargado pero no habilitado. Ejecuta systemctl enable --now <nombre>.timer.

El servicio se ejecuta pero el timer no lo activa: Asegúrate de que los nombres de los archivos .timer y .service coincidan exactamente. Verifica con systemctl cat <nombre>.timer si hay errores tipográficos.

“Failed to parse calendar specification”: La sintaxis de tu OnCalendar es incorrecta. Valídala con systemd-analyze calendar "tu expresión".

El timer se dispara con retraso: El valor predeterminado de AccuracySec es 1 minuto. systemd agrupa las activaciones para ahorrar energía. Configura AccuracySec=1s si necesitas precisión.

Las ejecuciones perdidas no se recuperan: Olvidaste añadir Persistent=true en la sección [Timer].

Resumen

  • Los systemd timers reemplazan a cron con dos archivos de unidad: un .service (qué ejecutar) y un .timer (cuándo ejecutarlo)
  • OnCalendar gestiona las programaciones basadas en reloj; OnBootSec y OnUnitActiveSec gestionan la temporización basada en intervalos
  • Persistent=true recupera ejecuciones perdidas después de una caída del sistema
  • systemd-analyze calendar te permite validar las expresiones de programación antes de desplegarlas
  • El registro integrado mediante journalctl elimina la necesidad de redirección personalizada de logs
  • Los controles de recursos (CPUQuota, MemoryMax) y las notificaciones de fallos te ofrecen fiabilidad de nivel de producción
  • Cualquier trabajo de cron puede migrarse a un systemd timer con una conversión de sintaxis sencilla
  • Usa systemd-run para tareas programadas puntuales y rápidas sin crear archivos de unidad

Configurar Trabajos Cron en Linux | journalctl: Consultar y Analizar Logs del Sistema Linux | Gestión de Servicios Linux con systemctl