Timers de Systemd como Alternativa a Cron: Programación Moderna de Tareas
Introducción
Los timers de systemd proporcionan una alternativa moderna y poderosa a los trabajos cron tradicionales para programar tareas en sistemas Linux. Mientras que cron ha servido a sistemas tipo Unix durante décadas, los timers de systemd ofrecen características mejoradas incluyendo gestión de dependencias, control de recursos, registro detallado e integración con el ecosistema systemd. Comprender los timers de systemd es esencial para las prácticas modernas de administración de sistemas y DevOps.
Esta guía completa explora los timers de systemd desde lo básico hasta patrones avanzados, demostrando cómo migrar desde cron e implementar escenarios de programación sofisticados.
¿Por Qué Elegir Timers de Systemd en Lugar de Cron?
Ventajas de los Timers de Systemd
Mejor Registro: Integración con journald proporciona logs estructurados y consultables Gestión de Dependencias: Iniciar tareas solo cuando servicios específicos están disponibles Control de Recursos: Usar cgroups para límites de CPU, memoria e I/O Disparadores Basados en Eventos: Activar en arranque, inicio de servicio o cambios de archivo Expresiones de Calendario: Sintaxis de programación más flexible Manejo de Fallos: Reintento automático con retroceso configurable Gestión de Entorno: Mejor control sobre el entorno de ejecución Monitoreo: Verificación de estado nativa e integración con herramientas de monitoreo
Cuándo Usar Timers de Systemd
- Distribuciones Linux modernas (Ubuntu 16.04+, CentOS 7+, Debian 8+)
- Tareas que requieren límites de recursos
- Cadenas de dependencias complejas
- Necesidad de registro detallado y monitoreo
- Integración con otros servicios systemd
Prerequisitos
- Sistema Linux con systemd (verificar:
systemctl --version) - Acceso root o sudo
- Comprensión básica de servicios systemd
- Familiaridad con conceptos de cron
Fundamentos de Timers de Systemd
Componentes de Timers
Cada timer de systemd consiste en dos archivos:
- Unidad de Servicio (
.service): Define qué ejecutar - Unidad de Timer (
.timer): Define cuándo ejecutar
Ubicaciones de Archivos
# Timers del sistema
/etc/systemd/system/
# Timers de usuario
~/.config/systemd/user/
# Valores por defecto del sistema
/lib/systemd/system/
Creando Tu Primer Timer
Ejemplo: Timer de Respaldo Diario
Paso 1: Crear la Unidad de Servicio
# /etc/systemd/system/backup.service
[Unit]
Description=Daily Backup Service
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Group=backup
StandardOutput=journal
StandardError=journal
# Límites de recursos
CPUQuota=50%
MemoryLimit=512M
Paso 2: Crear la Unidad de Timer
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer
Requires=backup.service
[Timer]
# Ejecutar diariamente a las 2:00 AM
OnCalendar=daily
OnCalendar=*-*-* 02:00:00
Persistent=true
# Ejecutar inmediatamente si se perdió
Persistent=true
[Install]
WantedBy=timers.target
Paso 3: Habilitar e Iniciar
# Recargar systemd
sudo systemctl daemon-reload
# Habilitar timer (iniciar al arrancar)
sudo systemctl enable backup.timer
# Iniciar timer ahora
sudo systemctl start backup.timer
# Verificar estado
sudo systemctl status backup.timer
# Listar todos los timers
systemctl list-timers
# Ver logs
journalctl -u backup.service
Sintaxis de Expresiones de Calendario
Especificaciones de Tiempo
# Ejemplos de OnCalendar
# Cada minuto
OnCalendar=*-*-* *:*:00
# Cada 5 minutos
OnCalendar=*-*-* *:0/5:00
# Cada hora
OnCalendar=hourly
OnCalendar=*-*-* *:00:00
# Cada día a medianoche
OnCalendar=daily
OnCalendar=*-*-* 00:00:00
# Cada día a hora específica
OnCalendar=*-*-* 03:30:00
# Cada lunes a las 9 AM
OnCalendar=Mon *-*-* 09:00:00
# Días laborables a las 6 PM
OnCalendar=Mon..Fri *-*-* 18:00:00
# Primer día del mes
OnCalendar=*-*-01 00:00:00
# Cada 15 minutos
OnCalendar=*:0/15
# Múltiples horarios
OnCalendar=*-*-* 06:00:00
OnCalendar=*-*-* 18:00:00
# Horario de oficina (9-5, Lun-Vie)
OnCalendar=Mon..Fri 09..17:00:00
Probar Expresiones de Calendario
# Probar expresión de calendario
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
# La salida muestra:
# Original form: Mon..Fri *-*-* 09:00:00
# Normalized form: Mon..Fri *-*-* 09:00:00
# Next elapse: Mon 2024-01-15 09:00:00
# From now: 3 days left
Configuraciones Avanzadas de Timers
1. Timer de Verificación de Salud del Sistema
# /etc/systemd/system/health-check.service
[Unit]
Description=System Health Check
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/health-check.sh
User=monitoring
StandardOutput=journal
StandardError=journal
# Tiempo de espera después de 5 minutos
TimeoutStartSec=300
# Reiniciar en caso de fallo
Restart=on-failure
RestartSec=60
# /etc/systemd/system/health-check.timer
[Unit]
Description=Run Health Check Every 5 Minutes
[Timer]
# Cada 5 minutos
OnBootSec=5min
OnUnitActiveSec=5min
# Ejecutar inmediatamente al arrancar
OnBootSec=1min
[Install]
WantedBy=timers.target
2. Respaldo de Base de Datos con Dependencias
# /etc/systemd/system/db-backup.service
[Unit]
Description=Database Backup
Requires=postgresql.service
After=postgresql.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-database.sh
# Ejecutar como usuario específico
User=postgres
Group=postgres
# Establecer entorno
Environment="PGDATA=/var/lib/postgresql/data"
EnvironmentFile=/etc/db-backup.conf
# Límites de recursos
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
# Registro
StandardOutput=journal+console
StandardError=journal+console
# /etc/systemd/system/db-backup.timer
[Unit]
Description=Daily Database Backup at 2 AM
Requires=db-backup.service
[Timer]
# Diariamente a las 2 AM
OnCalendar=*-*-* 02:00:00
# Ponerse al día si se perdió
Persistent=true
# Aleatorizar hasta 30 minutos
RandomizedDelaySec=1800
[Install]
WantedBy=timers.target
3. Timer de Rotación de Logs
# /etc/systemd/system/log-rotation.service
[Unit]
Description=Rotate Application Logs
[Service]
Type=oneshot
ExecStart=/usr/local/bin/rotate-logs.sh
# Ejecutar con privilegios restringidos
User=logger
Group=logger
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/log/app
StandardOutput=journal
# /etc/systemd/system/log-rotation.timer
[Unit]
Description=Rotate Logs Daily at Midnight
[Timer]
# Diariamente a medianoche
OnCalendar=daily
# También ejecutar semanalmente incluso si no se activó
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.target
4. Timer de Limpieza con Múltiples Programaciones
# /etc/systemd/system/cleanup.service
[Unit]
Description=Cleanup Temporary Files
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup-temp.sh
ExecStart=/usr/local/bin/cleanup-old-logs.sh
ExecStart=/usr/local/bin/cleanup-cache.sh
User=root
StandardOutput=journal
# /etc/systemd/system/cleanup.timer
[Unit]
Description=Regular System Cleanup
[Timer]
# Ejecutar diariamente a las 3 AM
OnCalendar=*-*-* 03:00:00
# También ejecutar semanalmente los domingos
OnCalendar=Sun *-*-* 01:00:00
# Y mensualmente el primer día
OnCalendar=*-*-01 02:00:00
Persistent=true
AccuracySec=1h
[Install]
WantedBy=timers.target
Migrando de Cron a Timers de Systemd
Conversión de Cron a Timer
| Expresión Cron | Equivalente Timer Systemd |
|---|---|
*/5 * * * * | OnUnitActiveSec=5min |
0 2 * * * | OnCalendar=*-*-* 02:00:00 |
0 0 * * 0 | OnCalendar=Sun *-*-* 00:00:00 |
0 0 1 * * | OnCalendar=*-*-01 00:00:00 |
@reboot | OnBootSec=1min |
@daily | OnCalendar=daily |
@hourly | OnCalendar=hourly |
Script de Conversión
#!/bin/bash
# cron-to-systemd.sh
# Convertir trabajo cron a timer systemd
CRON_SCHEDULE="$1"
SERVICE_NAME="$2"
COMMAND="$3"
# Crear archivo de servicio
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=${SERVICE_NAME} Service
[Service]
Type=oneshot
ExecStart=${COMMAND}
StandardOutput=journal
StandardError=journal
EOF
# Crear archivo de timer basado en programación cron
# (Conversión simplificada)
TIMER_SPEC=""
case "$CRON_SCHEDULE" in
"@hourly")
TIMER_SPEC="OnCalendar=hourly"
;;
"@daily")
TIMER_SPEC="OnCalendar=daily"
;;
"@weekly")
TIMER_SPEC="OnCalendar=weekly"
;;
*)
echo "Manual conversion required for: $CRON_SCHEDULE"
;;
esac
cat > "/etc/systemd/system/${SERVICE_NAME}.timer" <<EOF
[Unit]
Description=${SERVICE_NAME} Timer
[Timer]
${TIMER_SPEC}
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
echo "Created ${SERVICE_NAME}.service and ${SERVICE_NAME}.timer"
Características Avanzadas
1. Disparadores Basados en Eventos
# /etc/systemd/system/process-uploads.service
[Unit]
Description=Process New Uploads
[Service]
Type=oneshot
ExecStart=/usr/local/bin/process-uploads.sh
# /etc/systemd/system/process-uploads.path
[Unit]
Description=Monitor Uploads Directory
[Path]
# Observar nuevos archivos
PathChanged=/var/uploads
# Activar servicio
Unit=process-uploads.service
[Install]
WantedBy=multi-user.target
2. Gestión de Recursos
# /etc/systemd/system/heavy-task.service
[Unit]
Description=Resource-Intensive Task
[Service]
Type=oneshot
ExecStart=/usr/local/bin/heavy-task.sh
# Límite de CPU (50%)
CPUQuota=50%
# Límite de memoria
MemoryLimit=1G
MemoryMax=1G
# Prioridad de I/O (mejor esfuerzo, baja prioridad)
IOSchedulingClass=2
IOSchedulingPriority=7
# Valor nice
Nice=19
# Limitar archivos abiertos
LimitNOFILE=1024
3. Manejo de Fallos y Reintentos
# /etc/systemd/system/api-sync.service
[Unit]
Description=Sync with External API
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/api-sync.sh
# Reintentar en caso de fallo
Restart=on-failure
RestartSec=60
# Máximo de intentos de reinicio
StartLimitBurst=3
StartLimitIntervalSec=300
# Tiempo de espera
TimeoutStartSec=180
Gestionando Timers de Systemd
Comandos Esenciales
# Listar todos los timers
systemctl list-timers --all
# Mostrar detalles del timer
systemctl status backup.timer
# Mostrar estado del servicio
systemctl status backup.service
# Ver logs
journalctl -u backup.service
journalctl -u backup.service --since "1 hour ago"
journalctl -u backup.service -f # Seguir
# Iniciar timer manualmente
systemctl start backup.timer
# Detener timer
systemctl stop backup.timer
# Habilitar al arrancar
systemctl enable backup.timer
# Deshabilitar
systemctl disable backup.timer
# Reiniciar timer
systemctl restart backup.timer
# Activar servicio inmediatamente
systemctl start backup.service
# Verificar cuándo se ejecutará el timer
systemctl list-timers backup.timer
# Recargar configuración
systemctl daemon-reload
Monitoreo y Depuración
# Verificar programación de calendario del timer
systemd-analyze calendar "*-*-* 02:00:00"
# Verificar configuración del timer
systemd-analyze verify /etc/systemd/system/backup.timer
# Ver propiedades del timer
systemctl show backup.timer
# Verificar dependencias del servicio
systemctl list-dependencies backup.service
# Modo depuración
SYSTEMD_LOG_LEVEL=debug systemctl start backup.service
Timers de Usuario
Creando Timers de Usuario
# Crear directorio systemd de usuario
mkdir -p ~/.config/systemd/user
# Crear servicio
cat > ~/.config/systemd/user/my-task.service <<EOF
[Unit]
Description=My Personal Task
[Service]
Type=oneshot
ExecStart=/home/user/bin/my-task.sh
EOF
# Crear timer
cat > ~/.config/systemd/user/my-task.timer <<EOF
[Unit]
Description=My Task Timer
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
# Recargar demonio de usuario
systemctl --user daemon-reload
# Habilitar e iniciar
systemctl --user enable --now my-task.timer
# Listar timers de usuario
systemctl --user list-timers
Habilitar Lingering
# Permitir que timers de usuario se ejecuten cuando no está conectado
sudo loginctl enable-linger $USER
# Verificar estado de lingering
loginctl show-user $USER | grep Linger
Mejores Prácticas
1. Convenciones de Nomenclatura
# Usar nombres descriptivos
backup-database.timer
cleanup-logs.timer
check-ssl-certificates.timer
2. Documentación
[Unit]
Description=Clear description of what this timer does
Documentation=https://wiki.company.com/backup-procedure
3. Notificaciones de Fallos
# /etc/systemd/system/backup.service
[Service]
ExecStart=/usr/local/bin/backup.sh
OnFailure=failure-notification@%n.service
4. Pruebas
# Probar servicio antes de habilitar timer
systemctl start backup.service
# Verificar logs
journalctl -u backup.service -n 50
# Verificar estado de salida
systemctl show -p Result backup.service
5. Seguridad
[Service]
# Ejecutar como usuario específico
User=backup
Group=backup
# Aislamiento
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/backup
# Capacidades
NoNewPrivileges=yes
Solución de Problemas
Problemas Comunes
Timer no se ejecuta:
# Verificar si está habilitado
systemctl is-enabled backup.timer
# Verificar estado
systemctl status backup.timer
# Ver logs de systemd
journalctl -xe
Servicio falla:
# Verificar logs del servicio
journalctl -u backup.service -n 100
# Probar servicio manualmente
systemctl start backup.service
# Verificar sintaxis del archivo de servicio
systemd-analyze verify /etc/systemd/system/backup.service
Timer se ejecuta en momento incorrecto:
# Verificar expresión de calendario
systemd-analyze calendar "daily"
# Verificar configuración del timer
systemctl cat backup.timer
# Listar próximo tiempo de activación
systemctl list-timers backup.timer
Conclusión
Los timers de systemd proporcionan una alternativa moderna y rica en características a cron para programar tareas en sistemas Linux. Con mejor registro, gestión de recursos e integración con el ecosistema systemd, los timers ofrecen ventajas significativas para la administración de sistemas y automatización.
Conclusiones clave:
- Los timers de systemd consisten en unidades .service y .timer
- Las expresiones de calendario son más flexibles que la sintaxis de cron
- La integración con journald proporciona mejor registro
- Los límites de recursos y características de seguridad están integrados
- Las dependencias y el orden se gestionan explícitamente
- Los timers de usuario habilitan programación de tareas sin root
Comienza a migrar trabajos cron críticos a timers de systemd para beneficiarte de características modernas mientras mantienes la confiabilidad. Combina timers con otras características de systemd como unidades path y activación de sockets para patrones de automatización aún más poderosos.


