Cron vs timers systemd : lequel choisir ?

Cron vs timers systemd : syntaxe, logs, rattrapage et dépendances — plus un tableau de décision pour placer chaque tâche planifiée au bon endroit.

HUMANpar @mrsaynothing45d6 min de lecture

Sur une distribution moderne, choisis un timer systemd pour tout ce que tu possèdes, et garde cron pour les jobs d'une ligne et les serveurs que tu n'as pas montés toi-même. Les timers journalisent chaque exécution dans le journal, peuvent rattraper les horaires manqués pendant que la machine était éteinte, et dépendent des mêmes fichiers unit que tout le reste du système. Cron gagne sur la brièveté — une ligne de crontab -e bat deux fichiers unit — et reste le seul ordonnanceur garanti d'exister sur les conteneurs minimaux et les boîtes Unix exotiques. Le hic : cron échoue en silence. Si ton job plante à 3 heures du matin, cron poste dans une boîte mail locale que personne ne lit, tandis qu'un timer te donne journalctl -u mytimer.service avec la sortie complète. Ci-dessous : les vraies différences, comment tester chacun, pourquoi un timer peut ne pas se déclencher, et un tableau de décision.

Quelle est la différence entre cron et un timer systemd ?

Cron est un daemon qui lit une table de lignes — cinq champs de temps et une commande — et exécute chaque commande quand l'horloge correspond. Tout le modèle est là. Il n'a pas de notion de job en tant qu'objet : pas d'unité, pas de statut, pas de dépendances, pas d'entrée de log propre.

Un timer systemd est un fichier unit (foo.timer) qui déclenche un autre fichier unit (foo.service) quand son horaire correspond. Le job est un objet de première classe avec start, status, logs et le suivi d'échec comme n'importe quel service. L'ordonnancement est soit calendaire (à la cron), soit monotone (OnBootSec=15min, que les changements d'horloge ne peuvent pas perturber).

Les conséquences pratiques :

  • Logs : les timers journalisent stdout/stderr dans le journal, par unité ; cron au mieux poste un mail à l'utilisateur local.
  • Exécutions manquées : un timer avec Persistent=true s'exécute une fois au boot si son horaire a été manqué ; cron passe juste à la suite.
  • Dépendances : les timers peuvent attendre network-online.target ou des points de montage ; cron t'oblige à bricoler ta logique de retry dans le script.
  • Syntaxe : cron tient en une ligne ; un timer demande deux fichiers. C'est tout le coût du changement.

Quelle syntaxe est plus simple : crontab ou OnCalendar ?

Les cinq champs de cron sont l'ancien à l'écriture compacte : */15 * * * * veut dire toutes les 15 minutes, et la plupart des admins les lisent les yeux fermés. Le OnCalendar= de systemd est plus verbeux mais strictement plus expressif, et systemd-analyze calendar te dira les prochaines exécutions avant que tu t'engages — cron n'a pas d'équivalent en dry-run.

# Cron : tous les jours à 03:30
30 3 * * * /usr/local/bin/backup.sh

# systemd : le même horaire, vérifiable avant de sauvegarder
systemd-analyze calendar "*-*-* 03:30:00"
# -> Prochaine exécution : ven. 2026-09-11 03:30:00 ...

OnCalendar gère ce que cron ne sait vraiment pas exprimer proprement : Mon..Fri *-*-* 09..17:00:00 (jours ouvrés, heures de bureau), ou *:0/15 avec RandomizedDelaySec=10m pour éviter que mille machines ne marteau un serveur à la même seconde.

Comment tester un timer systemd sans attendre ?

Trois commandes répondent à tout. Lister ce qui est planifié et quand ça se déclenche, lancer le job à la main exactement comme le timer le ferait, puis lire ses logs :

systemctl list-timers --all                 # tous les timers, prochaine + dernière exécution
sudo systemctl start backup.service         # lancer le job maintenant, la même unité que le timer
journalctl -u backup.service -f             # suivre sa sortie en direct

Note la séparation : systemctl start backup.timer arme l'horaire ; le service est le job. Si list-timers montre ton timer, que systemctl status backup.service est vert et que le journal affiche ta sortie, toute la chaîne fonctionne.

Comment lancer un job cron à la main ?

Les jobs cron tournent avec un environnement amputé, d'où le genre de bug « ça marche dans mon shell, ça plante dans cron ». Pour reproduire cron fidèlement, lance la commande à travers sh avec le même environnement que cron utiliserait :

crontab -l                                  # confirmer la ligne exacte
env -i SHELL=/bin/sh PATH=/usr/bin:/bin sh -c '/usr/local/bin/backup.sh'
grep CRON /var/log/syslog | tail            # cron l'a-t-il seulement lancée ? (Debian/Ubuntu)
journalctl -u cron -n 20                    # pareil, sur les distros systemd

Cette ligne env -i est le test manuel honnête : un environnement nu avec le PATH par défaut de cron. La plupart des échecs silencieux de cron sont un PATH nu ou un % non quoté dans la commande (cron traite % comme un retour à la ligne), et les deux apparaissent immédiatement ainsi.

Pourquoi mon timer systemd ne se déclenche-t-il pas ?

Quatre causes couvrent presque tous les cas, dans l'ordre où les vérifier :

  1. Le nom du service et celui du timer ne correspondent pas. foo.timer déclenche foo.service — un OnFailure mal tapé ou une unité renommée, et le timer « tourne » dans le vide. systemctl cat foo.timer montre exactement ce qu'il vise.
  2. Le timer a été édité mais pas rechargé. Après avoir changé un fichier unit : sudo systemctl daemon-reload && sudo systemctl restart foo.timer. Sans ça, ton nouvel horaire n'est pas actif.
  3. Persistent=true sans la sémantique OnCalendar attendue — ou tu regardes systemctl status foo.timer (toujours actif même au repos) au lieu de list-timers.
  4. Le service qu'il déclenche plante instantanément, si bien que le timer paraît mort. journalctl -u foo.service --since -1h montrera le crash que list-timers cache.

Et quand tu regardes les logs, l'aide-mémoire journalctl couvre les filtres (-u, --since, -f) qui rendent ça rapide.

Cron vs timers systemd : le tableau de décision

cron timer systemd
Mise en place une ligne crontab deux fichiers unit
Logs mail local, rarement lu journal, par unité
Exécution manquée (machine éteinte) ignorée exécutée une fois avec Persistent=true
Dépendances / ordre aucune — DIY dans le script dépendances d'unités complètes
Délai aléatoire DIY avec $RANDOM RandomizedDelaySec=
Test/dry-run de l'horaire non systemd-analyze calendar
Existe partout oui — conteneurs, BSD, embarqué exige systemd (PID 1)
Jobs utilisateur sans root crontab -e unités systemd --user

Règles de pouce : livrer un job sur ta propre machine systemd → timer. Rappel perso rapide ou une boîte que tu n'as pas construite → cron. Tout ce qui a des dépendances, des retries, ou un besoin de savoir si ça a vraiment tourné → timer, toujours. Un motif courant est un timer qui lance un job de maintenance — disons une sauvegarde rsync nocturne — où Persistent=true garantit que la sauvegarde a lieu même si la machine dormait à la minute prévue, ce que cron ne peut tout simplement pas offrir.