CyberSDF

Reprenez la main sur votre numérique

Crontab ou systemd timer : six écarts mesurables pour trancher

des engrenages et des fleches en aplats simples relies en chaine, composition horizontale

Une ligne 0 3 * * * posée dans un crontab part à 3 h 00. Le même horaire écrit en OnCalendar=*-*-* 03:00:00 dans un timer systemd peut démarrer jusqu’à une minute plus tard, parce que le réglage AccuracySec, décrit par la page systemd.timer(5), vaut 1min par défaut et sert à regrouper les réveils de la machine.

Sommaire
  1. Six écarts mesurables séparent crontab d’un timer systemd
  2. Un OnCalendar posé à 3 h 00 peut partir à 3 h 01
  3. Et si la machine était éteinte à l’heure prévue ?
  4. Une session fermée et un crontab sans retour à la ligne cassent le job
  5. Failed to enter waiting state : quand WakeSystem n’a pas le soutien du matériel
  6. Le changement d’environnement explique la moitié des jobs qui échouent
  7. Sources

Le reste se joue sur cinq autres mesures, dont la destination de la sortie du job et la reprise d’une exécution manquée pendant une extinction. La rubrique automatiser sous Linux rassemble les autres montages d’exécution différée, du script lancé à la main au service déclenché par événement.

Six écarts mesurables séparent crontab d’un timer systemd

Le journal du job et la reprise après extinction tranchent entre crontab et timer systemd, pas la syntaxe du calendrier. Un cron envoie la sortie de la commande par courriel au propriétaire du crontab, sauf si la variable MAILTO nomme une autre adresse ou reste vide. Un timer remet cette sortie au journal systemd, où journalctl la relit unité par unité. Une exécution perdue pendant une extinction se rattrape avec Persistent=true, une option qui vaut false par défaut.

La granularité creuse un deuxième écart : le démon cron relit ses entrées chaque minute, comme l’écrit la page crontab(5) du paquet cron 3.0pl1-162 livré dans Debian 12, tandis qu’un OnCalendar accepte une seconde dans son format. Le nombre de fichiers à écrire en creuse un troisième : une ligne dans crontab -e d’un côté, un fichier foo.service et un fichier foo.timer de l’autre, avec les commandes systemctl qui les activent.

Valeurs relevées dans les pages de manuel du paquet cron 3.0pl1-162 (Debian 12), de systemd.timer(5) et du wiki Arch.
Mesure crontab timer systemd
Granularité minimale 1 minute, le démon relit les entrées à chaque minute (crontab(5)) 1 seconde, le format OnCalendar porte un champ seconde (wiki Arch)
Retard maximal du départ aucun réglage, la commande part à la minute inscrite 60 secondes au plus, AccuracySec valant 1min par défaut (systemd.timer(5))
Destination de la sortie courriel au propriétaire du crontab, ou à l’adresse de MAILTO (crontab(5)) journal systemd, relu avec journalctl -u
Exécution manquée pendant une extinction aucun mécanisme dans crontab(5), anacron(8) couvre ce cas Persistent=true, false par défaut et sans effet hors OnCalendar (systemd.timer(5))
Alerte en cas d’échec courriel automatique, une valeur MAILTO vide coupe l’envoi (crontab(5)) aucun équivalent de MAILTO intégré, il faut OnFailure= (wiki Arch)
Nombre de fichiers à écrire 1, une ligne dans crontab -e 2, foo.service et foo.timer, puis systemctl enable

Un piège de calendrier reste propre à crontab. Quand le jour du mois et le jour de la semaine portent tous les deux une valeur, la page crontab(5) écrit que la commande part dès que l’un des deux correspond. La ligne 30 4 1,15 * 5 déclenche donc le job le 1er, le 15 et chaque vendredi, et non les seuls vendredis qui tombent un 1er ou un 15.

Un OnCalendar posé à 3 h 00 peut partir à 3 h 01

AccuracySec décale le départ réel d’un timer systemd, jamais l’expression OnCalendar. La page systemd.timer(5) décrit une fenêtre qui commence à l’heure inscrite et se termine AccuracySec plus tard, la position exacte du déclenchement étant tirée au hasard une fois pour la machine puis conservée. Ce réglage vaut 1min par défaut, pour grouper les réveils du processeur et économiser de l’énergie. RandomizedDelaySec fait l’inverse : il étale les départs de plusieurs timers pour qu’ils ne tombent pas ensemble, et vaut 0 par défaut.

Quand la minute exacte compte, la page systemd.timer(5) indique la valeur AccuracySec=1us. La commande systemd-analyze calendar, décrite par la page systemd-analyze(1), fait relire l’expression avant de l’écrire dans un fichier d’unité.

systemd-analyze calendar "Mon,Tue *-*-01..04 12:00:00"
systemctl list-timers --all

La première commande affiche une ligne Normalized form, une ligne Next elapse et le délai restant avant l’échéance. La seconde montre, pour chaque timer, les colonnes NEXT, LEFT, LAST et PASSED du wiki Arch, et le journal du service se lit avec les mêmes outils que les autres unités, décrits dans services et journaux systemd. Une limite subsiste : un timer ne relance pas un service déjà en cours, la page systemd.timer(5) précisant que l’unité à activer est laissée en fonctionnement si elle tourne au moment de l’échéance.

Et si la machine était éteinte à l’heure prévue ?

Une exécution manquée est perdue avec crontab, alors qu’un timer systemd la rattrape dès son activation quand Persistent=true figure dans la section [Timer]. L’option vaut false par défaut et ne s’applique qu’aux timers munis d’un OnCalendar, d’après la page systemd.timer(5).

Debian installe anacron pour combler ce trou côté cron, et sa page anacron(8) écrit qu’anacron, contrairement à cron, ne suppose pas que la machine tourne en permanence. Le même document porte deux réserves peu reprises ailleurs : l’activation d’anacron a lieu chaque heure entre 07 h 30 et 23 h 30, et elle est désactivée par défaut quand l’ordinateur fonctionne sur batterie sans secteur. Systemd garde la sienne, une seule activation du service si le timer a manqué plusieurs échéances pendant une longue veille.

[Unit]
Description=Sauvegarde nocturne

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=5min

[Install]
WantedBy=timers.target

Le fichier se place dans /etc/systemd/system, comme le montrent les exemples du wiki Arch, puis s’active avec systemctl enable et systemctl start sur le nom du timer. Le contrôle des sorties et la reprise après un échec se traitent à part, comme le décrit contrôle et reprise après échec. Avant de supprimer un timer qui utilisait Persistent, la page systemd.timer(5) demande de lancer systemctl clean --what=state sur l’unité, pour effacer l’horodatage conservé sur disque.

Une session fermée et un crontab sans retour à la ligne cassent le job

Un timer d’utilisateur s’arrête avec la dernière session ouverte, sans message, parce que le gestionnaire systemd de l’utilisateur est détruit à ce moment-là. Le wiki Arch le formule ainsi : par défaut, ces unités ne tournent que pendant une session ouverte, et loginctl enable-linger permet de passer outre. La page loginctl(1) précise ce que fait l’option : un gestionnaire d’utilisateur est lancé au démarrage de la machine et maintenu après les déconnexions, ce qui autorise les services de longue durée hors session.

Le piège côté cron ne se voit pas davantage. La page crontab(1) du paquet cron 3.0pl1-162 écrit que chaque entrée doit se terminer par un caractère de fin de ligne et que, si la dernière entrée n’en a pas, cron refuse d’installer le crontab.

mkdir -p ~/.config/systemd/user
loginctl enable-linger
systemctl --user enable sauvegarde.timer
systemctl --user start sauvegarde.timer
journalctl --user -u sauvegarde.service

Les unités d’utilisateur vivent dans ~/.config/systemd/user, d’après le wiki Arch, et deux réserves les accompagnent : elles ne peuvent ni référencer ni dépendre d’unités système, et leur fichier d’horodatage se range dans ~/.local/share/systemd/ au lieu de /var/lib/systemd/timers/.

Failed to enter waiting state : quand WakeSystem n’a pas le soutien du matériel

WakeSystem=true sort une machine en veille pour lancer le service, ce que crontab ne sait pas faire. Le wiki Arch signale que cette option demande des capacités système particulières et qu’elle est en général réservée au gestionnaire système ; quand elles manquent, le timer ne démarre pas et laisse deux messages littéraux dans le journal. Le remède tient en un geste : retirer la ligne WakeSystem du fichier d’unité, ou déplacer le timer du côté système.

Failed to enter waiting state: Operation not supported
Failed with result 'resources'.

Une deuxième différence tient à la version installée. DeferReactivation= empêche la relance immédiate d’un service qui dure plus longtemps que son intervalle, et la page systemd.timer(5) date cette option de systemd 257 : avant elle, l’échéance suivante est calculée depuis le déclenchement précédent, donc déjà dépassée à la fin du service, ce qui redéclenche le travail aussitôt. La commande systemctl --version donne la version avant de recopier une unité trouvée en ligne.

Le changement d’environnement explique la moitié des jobs qui échouent

Un cron démarre la commande avec le shell /bin/sh et trois variables posées par le démon, HOME, LOGNAME et SHELL, comme l’écrit la page crontab(5) : une syntaxe bash ou un alias défini dans .bashrc n’existe pas au moment du départ. Une unité d’utilisateur systemd ignore elle aussi les variables exportées par .bashrc, d’après la page systemd/User du wiki Arch, et systemd ne se sert pas du PATH pour résoudre un binaire écrit sans chemin absolu.

Deux gestes couvrent la plupart des cas : écrire le chemin complet du programme, et poser les variables utiles avec systemctl --user import-environment PATH. Un script écrit pour /bin/sh évite ce genre de surprise, comme le détaille écrire un script shell.

Un timer systemd réveille-t-il plus souvent le processeur qu’une ligne de crontab ?

Non, il en réveille moins. Systemd place l’échéance dans la fenêtre AccuracySec et choisit une position identique pour tous les timers locaux, afin de regrouper les réveils et de réduire la consommation : la page systemd.timer(5) donne 1min par défaut et conseille AccuracySec=1us seulement quand la minute exacte compte. La page crontab(5) décrit de son côté un examen des entrées une fois par minute, sans fenêtre de regroupement.

Crontab reste-t-il le bon choix pour une seule tâche ?

Oui, quand la tâche tient en une ligne et que sa sortie doit partir par courriel. Le wiki Arch liste les deux frais du passage aux timers : deux fichiers à créer au lieu d’une ligne, et aucun équivalent intégré à MAILTO pour prévenir d’un échec. Une sauvegarde unique lancée chaque nuit se décrit donc encore très bien dans crontab -e, alors qu’une tâche qui doit survivre aux coupures et se relire dans le journal demande un timer.

Comment vérifier qu’une expression OnCalendar partira à l’heure voulue ?

La commande systemd-analyze calendar passe l’expression au crible et affiche quatre lignes, Original form, Normalized form, Next elapse et From now. Avec –iterations=5, la page systemd-analyze(1) montre les cinq prochaines échéances. Après installation, systemctl list-timers affiche pour chaque timer les colonnes NEXT, LEFT, LAST et PASSED, que le wiki Arch utilise pour repérer un timer désynchronisé.

Que se passe-t-il si le service dure plus longtemps que l’intervalle du timer ?

Avant systemd 257, le timer redéclenche le service dès qu’il se termine, parce que l’échéance suivante est calculée depuis le déclenchement précédent et se trouve déjà dépassée. La page systemd.timer(5) date de la version 257 l’option DeferReactivation=, qui reporte plutôt l’échéance à la fin du service. Un service encore en cours n’est jamais lancé en double : si l’unité à activer est déjà active au moment de l’échéance, le timer la laisse tourner.

Faut-il effacer quelque chose avant de supprimer un timer ?

Oui, dès que le timer utilise Persistent, puisqu’un horodatage de la dernière exécution reste sur disque. La page systemd.timer(5) demande de lancer systemctl clean –what=state sur l’unité avant de la désinstaller. Le wiki Arch situe ce fichier dans /var/lib/systemd/timers sous un nom préfixé par stamp-, et dans ~/.local/share/systemd/ pour un timer d’utilisateur.

Sources