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:
- Una unidad
.service— define qué ejecutar (el comando o script real) - 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:
| Directiva | Propósito |
|---|---|
OnCalendar | Programación basada en calendario (similar a cron) |
Persistent=true | Ejecutar inmediatamente si se perdió una ejecución programada (por ejemplo, si el sistema estaba apagado) |
WantedBy=timers.target | Necesario 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ón | Expresión OnCalendar |
|---|---|
| Todos los días a medianoche | *-*-* 00:00:00 |
| Cada lunes a las 9 AM | Mon *-*-* 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 PM | Mon..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
| Directiva | Se activa |
|---|---|
OnBootSec | X tiempo después del arranque |
OnStartupSec | X tiempo después de que inicie systemd |
OnUnitActiveSec | X tiempo después de la última activación del timer |
OnUnitInactiveSec | X 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:
| Cron | OnCalendar |
|---|---|
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 |
@reboot | Usar 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) OnCalendargestiona las programaciones basadas en reloj;OnBootSecyOnUnitActiveSecgestionan la temporización basada en intervalosPersistent=truerecupera ejecuciones perdidas después de una caída del sistemasystemd-analyze calendarte permite validar las expresiones de programación antes de desplegarlas- El registro integrado mediante
journalctlelimina 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-runpara 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