Une tâche cron qui échoue silencieusement est un classique du sysadmin depuis des décennies : cron exécute la commande à l’heure dite, et c’est tout. Aucune notification par défaut, aucun historique structuré, aucun mécanisme de retry. Un systemd timer résout ce problème en héritant directement des mécanismes de fiabilité déjà présents pour n’importe quel service systemd.
Ce que cron ne fait jamais de lui-même
cron déclenche une commande à l’heure planifiée, sans jamais vérifier si elle a réussi. Un job qui échoue (code de sortie non nul) ne redéclenche rien, ne notifie personne, et laisse au mieux une trace dans le mail système local (MAILTO), une convention ancienne rarement surveillée sur un serveur moderne où le mail local n’est même pas configuré.
# Ni retry, ni alerte, ni historique structuré :
# si cette commande échoue, rien ne le signale
0 3 * * * /usr/local/bin/backup.sh
Le timer systemd : la même exécution, avec l’historique et le retry en prime
Un timer systemd déclenche un service ordinaire à l’heure planifiée, ce qui signifie que ce service hérite de tout ce que systemd offre déjà : Restart=on-failure pour retenter automatiquement, un historique complet consultable via journalctl, et la même intégration avec systemctl status pour connaître l’état de la dernière exécution.
# backup.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Restart=on-failure
RestartSec=300
# backup.timer
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true : rattraper ce que cron ignore
Une machine éteinte ou en veille au moment où une tâche cron devait s’exécuter perd purement et simplement cette exécution : aucun mécanisme de rattrapage natif. Persistent=true sur un timer systemd change ce comportement : si le système était éteint au moment prévu, le timer déclenche l’exécution manquée dès le prochain démarrage, un détail qui compte particulièrement sur une machine qui ne tourne pas 24/7 (un poste de développement, une VM arrêtée la nuit pour réduire les coûts).
Consulter l’historique réel d’exécution
journalctl donne un historique complet et structuré de chaque exécution du service déclenché par le timer, contrairement à cron dont la seule trace, en l’absence de configuration explicite, se limite au mail système local.
# Historique complet de chaque exécution du service,
# succès et échecs, sans configuration supplémentaire
journalctl -u backup.service --since "7 days ago"
# Prochaine exécution planifiée et dernière exécution réelle,
# une information que cron ne donne jamais nativement
systemctl list-timers backup.timer
Ce que cron garde comme avantage réel
cron reste plus simple à lire et à modifier pour une tâche ponctuelle triviale, sans fichier .service séparé à maintenir en plus du timer, et reste omniprésent sur des systèmes où systemd n’est pas le gestionnaire d’init (certains conteneurs minimalistes, certains BSD). Pour une tâche véritablement critique où un échec silencieux a un coût réel (une sauvegarde, une purge de logs qui évite une saturation disque), le passage à un timer systemd élimine une classe entière d’incidents découverts trop tard, au prix d’une configuration légèrement plus verbeuse.
À retenir
Une tâche cron qui échoue ne le signale à personne par défaut, sans retry ni historique structuré, une limite qui existe depuis toujours et surprend encore régulièrement. Un timer systemd, en déclenchant un service ordinaire, hérite directement de Restart=, de l’historique complet via journalctl, et de Persistent=true pour rattraper une exécution manquée après une extinction. Le choix entre les deux dépend surtout du coût réel d’un échec silencieux : pour une tâche vraiment critique, cette visibilité change concrètement le temps de détection d’un problème, un des réflexes qui distinguent une infrastructure qui découvre ses incidents d’elle-même d’une infrastructure qui les découvre par accident.