SecOps Toolkit

Convertidor Bidireccional de Crontab a Systemd Timer

Migrar tareas de cron a temporizadores de systemd mejora el registro, el control y la recuperación de ejecuciones perdidas, pero traducir la sintaxis a mano es propenso a errores. Esta herramienta valida su expresión cron, la explica en lenguaje natural, calcula las próximas cinco ejecuciones y genera los archivos .service y .timer listos para copiar.

También funciona a la inversa: pegue una expresión OnCalendar y obtenga el cron equivalente cuando sea posible.

Por qué migrar de cron a systemd timers

Cron lleva décadas programando tareas en Linux, pero systemd ofrece ventajas operativas claras: cada ejecución queda registrada en el journal con su código de salida, las unidades pueden declarar dependencias y límites de recursos, se puede consultar el estado con systemctl list-timers y existe Persistent=true para recuperar ejecuciones perdidas cuando el equipo estaba apagado. Esta herramienta convierte en ambos sentidos entre una expresión cron y los archivos .service y .timer equivalentes.

Anatomía de una expresión cron

Una línea de crontab tiene cinco campos que se leen de izquierda a derecha:

  • minuto (0-59), hora (0-23), día del mes (1-31), mes (1-12 o nombres) y día de la semana (0-7 o nombres, donde 0 y 7 son domingo).
  • Operadores: * (todos), , (lista), - (rango) y / (paso). Por ejemplo, */15 en minutos equivale a 0, 15, 30 y 45.
  • Macros: @hourly, @daily, @weekly, @monthly y @yearly.

Existe una regla poco conocida: cuando tanto el día del mes como el día de la semana están restringidos, cron ejecuta la tarea si coincide cualquiera de los dos (operación OR). Systemd, en cambio, exige que ambos coincidan. Por eso la herramienta genera dos líneas OnCalendar en ese caso.

Anatomía de OnCalendar

El formato de systemd es DíaSemana AAAA-MM-DD HH:MM:SS, donde cada componente admite *, listas con coma, rangos con .. y repeticiones con /. Los segundos se completan con :00 para emular la granularidad de un minuto de cron.

  • 30 2 * * * se convierte en *-*-* 02:30:00.
  • */15 * * * * se convierte en *-*-* *:00/15:00.
  • 0 9 * * 1-5 se convierte en Mon..Fri *-*-* 09:00:00.
  • 0 4 1 * * se convierte en *-*-1 04:00:00.

Los dos archivos: servicio y temporizador

El archivo .service describe qué se ejecuta. Se declara como Type=oneshot porque la tarea arranca, termina y sale. Admite User=, WorkingDirectory= y ExecStart= con la ruta absoluta del ejecutable. El archivo .timer describe cuándo se ejecuta:

  • OnCalendar=: el calendario equivalente a la expresión cron.
  • Persistent=true: guarda en disco la última activación y, si se perdió alguna mientras el equipo estaba apagado, la lanza al arrancar.
  • AccuracySec=: margen de tolerancia para agrupar activaciones y ahorrar energía. El valor por defecto es 1 minuto; con 1s la ejecución es casi exacta.
  • Unit= y la sección [Install] con WantedBy=timers.target, para que el temporizador arranque con el sistema.

Diferencias de entorno que provocan fallos

  • PATH mínimo: systemd no hereda el entorno de su shell. Use rutas absolutas en ExecStart.
  • Sin shell por defecto: las tuberías, redirecciones y variables requieren /bin/sh -c '...', que la herramienta añade automáticamente.
  • Caracteres especiales: en un archivo de unidad, % y $ tienen significado propio y se escapan como %% y $$. En crontab, un % sin escapar equivale a un salto de línea.
  • Sin correo: cron envía la salida por correo (MAILTO); systemd la guarda en el journal, que se consulta con journalctl -u nombre.service.

Verificación recomendada

Antes de activar el temporizador, valide el calendario con systemd-analyze calendar "*-*-* 02:30:00", que muestra la forma normalizada y la próxima ejecución, y compruebe la sintaxis de las unidades con systemd-analyze verify. Tras activar, systemctl list-timers confirma cuándo se disparará la siguiente ejecución.

Casos de uso reales

  • Copias de seguridad nocturnas con Persistent=true, para que no se pierdan si el servidor estuvo apagado.
  • Rotación y limpieza de logs con ejecuciones de baja prioridad y AccuracySec amplio.
  • Renovación de certificados y sincronizaciones con registro auditable en el journal.
  • Auditorías de migración: convertir en bloque un crontab heredado y revisar qué tareas dependen de variables de entorno.

Zona horaria

Cron y systemd ejecutan según la zona horaria del servidor, salvo que se indique otra. La herramienta muestra las próximas cinco ejecuciones en la hora local de su navegador o en UTC para que pueda comprobar que el calendario coincide con lo esperado.

Preguntas Frecuentes

¿Qué hace exactamente Persistent=true en un timer de systemd?
Con Persistent=true, systemd guarda en disco la fecha de la última activación del temporizador. Si el equipo estaba apagado o suspendido cuando debía ejecutarse la tarea, al arrancar la lanza de inmediato para recuperar la ejecución perdida. Es el comportamiento equivalente a anacron y solo tiene efecto con temporizadores basados en calendario (OnCalendar). Resulta especialmente útil en portátiles, máquinas virtuales que se apagan y servidores con ventanas de mantenimiento.
¿Por qué un cron con día del mes y día de la semana genera dos líneas OnCalendar?
Porque cron y systemd interpretan esa combinación de forma distinta. En cron, si ambos campos están restringidos, la tarea se ejecuta cuando coincide cualquiera de los dos (OR). En OnCalendar, el día del mes y el día de la semana deben coincidir a la vez (AND). Para reproducir el comportamiento de cron, la herramienta genera una línea para el día del mes y otra para el día de la semana, y systemd activa el temporizador cuando cualquiera de ellas coincida.
¿Qué valor de AccuracySec debo elegir?
El valor por defecto de systemd es 1 minuto, que permite agrupar activaciones cercanas y reducir despertares de la CPU. Si necesita que la tarea se ejecute casi exactamente en el segundo programado, use 1s, que es la opción preseleccionada en la herramienta. Para trabajos poco sensibles al tiempo, como limpiezas o informes, 1min o incluso 1h ahorran energía. Si quiere repartir la carga de muchos servidores, combine un AccuracySec amplio con RandomizedDelaySec.
¿Por qué mi comando funciona en cron pero falla en el servicio de systemd?
Las causas más frecuentes son un PATH distinto, la falta de variables de entorno, el uso de funciones de shell sin invocar una shell y los caracteres especiales. Systemd no ejecuta el comando dentro de una shell, por lo que tuberías, redirecciones y expansión de variables requieren /bin/sh -c. Además, % y $ deben escaparse como %% y $$ en el archivo de unidad. Use rutas absolutas, revise el error con journalctl -u nombre.service y pruebe el servicio con systemctl start antes de activar el temporizador.
¿Puedo programar tareas como usuario normal sin ser root?
Sí. Coloque las unidades en ~/.config/systemd/user/ y gestiónelas con systemctl --user, por ejemplo systemctl --user enable --now nombre.timer. Para que se ejecuten aunque no tenga una sesión abierta, un administrador debe activar la persistencia de su usuario con loginctl enable-linger. En ese caso no se usa la directiva User= en el servicio, porque la unidad ya se ejecuta con su propio usuario. La herramienta genera archivos de sistema, pero puede adaptarlos fácilmente a esta modalidad.