Linux : planifier des tâches avec les timers systemd sur Debian
Sur Linux, cron n'est pas le seul moyen de planifier une tâche : systemd embarque son propre planificateur, les timers, et Debian l'utilise déjà pour ses propres besoins sans que vous l'ayez remarqué.
Que ce soit pour lancer une sauvegarde chaque nuit sur un serveur, purger des fichiers temporaires ou exécuter un script de synchronisation toutes les 15 minutes, la question se pose rapidement : faut-il continuer avec la crontab ou passer aux timers systemd ? Les deux approches coexistent sur Debian, et chacune a ses arguments.
Dans cet article, je vous propose de découvrir le fonctionnement d'un timer systemd, de créer pas à pas un timer complet sur Debian (service, timer, activation, vérification), puis d'explorer la syntaxe OnCalendar, les options qui font la différence face à cron (rattrapage des exécutions manquées, délai aléatoire, journalisation) et la partie debug. Cet article sera aussi l'occasion d'effectuer une comparaison entre timer systemd et cron.
Si vous débutez avec la planification de tâches sous Linux, je vous invite à lire au préalable mon tutoriel sur la planification de tâches avec cron et la crontab.
Qu'est-ce qu'un timer systemd ?
Un timer systemd est une unité de type .timer qui déclenche l'exécution d'une autre unité, généralement un service .service, à un moment précis (heure calendaire) ou suite à un événement (démarrage de la machine, dernière exécution). Le timer définit quand exécuter, le service définit quoi exécuter. C'est l'équivalent moderne d'une ligne de crontab, intégré au gestionnaire de services de Linux (systemd ; que certains n'aiment pas).
Avec la crontab, une seule ligne contient à la fois la planification et la commande, alors qu'ici il y a une séparation des rôles. Cela va même encore plus loin, car avec systemd, il faut deux fichiers :
Le service (
nom.service) : décrit la commande ou le script à exécuter, l'utilisateur, l'environnement, les dépendancesLe timer (
nom.timer) : décrit le calendrier d'exécution et les options de planification
Par convention, le timer active le service qui porte le même nom que lui (suffixe mis à part). Ce comportement est le comportement par défaut, il est possible de le modifier avec la directive Unit=. Toutefois, respectez les bonnes pratiques : utilisez des noms identiques.
Un timer peut être de deux natures :
Timer temps réel (directive
OnCalendar=) : basé sur l'horloge du système, avec une expression calendaire. C'est l'équivalent direct d'une entrée cronTimer monotone (directives
OnBootSec=,OnActiveSec=,OnUnitActiveSec=, etc.) : basé sur un délai relatif à un événement, indépendant de l'heure système et du fuseau horaire. Cron n'a pas de véritable équivalent, à l'exception de@reboot
Les timers déjà présents sur Debian
Avant de créer quoi que ce soit, regardons ce que Debian fait déjà avec les timers. Ce sera l'occasion d'exécuter une première commande destinée à lister les timers actifs, leur prochaine exécution et la dernière exécution.
systemctl list-timersSur une installation Debian standard, vous verrez des timers comme apt-daily.timer, apt-daily-upgrade.timer, logrotate.timer, man-db.timer, fstrim.timer ou e2scrub_all.timer. Un exemple récupéré sur l'une de mes machines sous Debian 13 :
Cette sortie est intéressante : la rotation des logs, que nous avons détaillée dans notre tutoriel sur logrotate, est pilotée par logrotate.timer sur Debian, et non plus par une entrée dans /etc/cron.daily/
Le nom des colonnes est relativement explicite :
NEXT : date de la prochaine exécution
LEFT : temps restant avant celle-ci
LAST : date de la dernière exécution
PASSED : temps écoulé depuis la dernière exécution
UNIT : le timer
ACTIVATES : le service déclenché par ce timer
La lecture des deux dernières colonnes permet d'ailleurs de voir le lien entre le timer et le service, ce qui correspond à la relation évoquée précédemment.
Pour inclure les timers inactifs (chargés mais non démarrés), ajoutez --all. Il est possible qu'il n'y en ait pas.
systemctl list-timers --allNote : cron reste installé par défaut sur Debian et les répertoires /etc/cron.daily/, /etc/cron.weekly/ et /etc/cron.hourly/ sont toujours traités. Les deux planificateurs cohabitent sans conflit. Toutefois, sur certaines images Debian 13, notamment sur des instances Cloud, ce n'est pas le cas : cron est absent, tandis que les timers sont là.
Créer un timer systemd pas à pas
Passons à la pratique avec un scénario réaliste : nous allons planifier une sauvegarde quotidienne du répertoire /etc sous forme d'archive compressée dans /srv/backup, chaque nuit à 2h00. C'est une tâche typique que l'on confierait à cron presque par habitude.
Créer le script à exécuter
Commençons par le script de sauvegarde. Je le place dans /usr/local/sbin/ et je le rends exécutable.
# Créer le répertoire de destination
sudo mkdir -p /srv/backup
# Créer le script
sudo nano /usr/local/sbin/backup-etc.shContenu du script, à titre d'exemple. Utilisez le script de votre choix, ici c'est à des fins d'illustration pour ce scénario.
#!/bin/bash
# Sauvegarde compressée de /etc avec horodatage
set -euo pipefail
DEST="/srv/backup"
ARCHIVE="${DEST}/etc-$(date +%Y%m%d-%H%M%S).tar.gz"
# Création de l'archive
tar -czf "${ARCHIVE}" /etc
# Suppression des archives de plus de 14 jours
find "${DEST}" -name 'etc-*.tar.gz' -mtime +14 -delete
echo "Sauvegarde terminée : ${ARCHIVE}"Enregistrez le script et rendez-le exécutable. Puis, quand c'est fait, testez le script.
sudo chmod +x /usr/local/sbin/backup-etc.sh
sudo /usr/local/sbin/backup-etc.shTestez toujours votre script à la main avant de le planifier : un timer ne fera pas fonctionner un script qui ne fonctionne pas. Au moins, nous partons avec la certitude que notre script est correct.
Créer le service
Le service décrit ce qui doit être exécuté. Les unités créées par l'administrateur se placent dans /etc/systemd/system/.
sudo nano /etc/systemd/system/backup-etc.serviceC'est là où l'approche peut surprendre. Le fichier de service doit simplement contenir une commande permettant d'exécuter notre script.
[Unit]
Description=Sauvegarde du répertoire /etc
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup-etc.shDétaillons ces directives :
Description=: texte libre affiché parsystemctl statuset dans les journaux.Type=oneshot: indique que le processus effectue une tâche puis se termine. C'est le type adapté aux scripts lancés par un timer, contrairement àType=simplequi est prévu pour un démon qui reste en vie.ExecStart=: la commande à exécuter, obligatoirement avec un chemin absolu. Il est possible de passer des arguments à la suite.
Vous remarquerez l'absence de section [Install]. Elle est inutile ici : ce service n'a pas vocation à être activé au démarrage, c'est le timer qui le déclenchera.
Astuce : un service oneshot peut être lancé à la main à tout moment avec sudo systemctl start backup-etc.service. C'est une manière pratique de tester l'exécution dans le contexte exact de systemd (utilisateur, environnement, journalisation), sans attendre l'heure prévue par le timer.
Créer le timer
Le fichier de service a besoin de son copain : le timer. Il porte le même nom que le service, avec l'extension .timer.
sudo nano /etc/systemd/system/backup-etc.timerIntégrez ceci dans le fichier :
[Unit]
Description=Exécution quotidienne de la sauvegarde de /etc
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.targetCe fichier correspond à ce que l'on avait déterminé précédemment, à savoir :
OnCalendar=*-*-* 02:00:00: exécution tous les jours à 2h00.Persistent=true: si la machine était éteinte à 2h00, la sauvegarde est lancée dès le prochain démarrage. C'est l'un des avantages majeurs en comparaison de cron.WantedBy=timers.target: rattache le timer à la cibletimers.target, ce qui permet de l'activer au démarrage avecsystemctl enable. Cette section[Install]est indispensable, contrairement au service.
Activer et démarrer le timer
Après avoir créé ou modifié des fichiers d'unit comme ceux que nous venons de créer, il faut demander à systemd de recharger sa configuration. Puis on active le timer (démarrage automatique) et on le démarre immédiatement en une seule commande grâce à --now. Si vous avez l'habitude de gérer des services sous Linux avec systemd, cela doit vous parler.
# Recharger la configuration de systemd
sudo systemctl daemon-reload
# Activer le timer au démarrage et le démarrer maintenant
sudo systemctl enable --now backup-etc.timer
Created symlink '/etc/systemd/system/timers.target.wants/backup-etc.timer' → '/etc/systemd/system/backup-etc.timer'.Point important : c'est bien le timer que l'on active, jamais le service. Et si vous vous demandez pourquoi il faut faire un enable sur un timer, sachez que c'est une unité systemd comme les autres. Ainsi, il ne fait quelque chose que lorsqu'il est actif. Un timer actif est un timer que systemd a chargé en mémoire, pour lequel il a calculé la prochaine échéance et armé une horloge interne. Un timer inactif n'est qu'un fichier sur le disque : aucune échéance n'est calculée, rien ne se déclenchera. Autrement dit, cela active la tâche planifiée.
Vérifier le timer
Vérifions que notre timer est bien pris en compte et regardons quand il s'exécutera :
# Vérifier l'état du timer
systemctl status backup-etc.timer
# Voir la prochaine exécution
systemctl list-timers backup-etc.timerCe qui donne :
Pour tester sans attendre 2h00 du matin, lancez directement le service :
sudo systemctl start backup-etc.servicePuis, consultez le résultat :
systemctl status backup-etc.serviceVoici le résultat obtenu :
Comment l'interpréter ?
Après une exécution, le statut du service indique inactive (dead) avec la mention (code=exited, status=0/SUCCESS) si tout s'est bien passé. Pour un service oneshot, c'est l'état normal : il ne reste pas actif entre deux exécutions.
Maîtriser la syntaxe OnCalendar
La directive OnCalendar= accepte des expressions calendaires dont le format est décrit dans le manuel systemd.time. Le format complet est :
JourDeLaSemaine Année-Mois-Jour Heure:Minute:SecondeChaque composant peut être remplacé par * (toute valeur), une liste séparée par des virgules (1,15), une plage (Mon..Fri, 08..18) ou un pas avec / (*/2 pour toutes les deux unités). Le jour de la semaine est facultatif. Si la partie date est omise, *-*-* est implicite, et si la partie heure est omise, 00:00:00 est implicite.
Expressions courantes et équivalents cron
Le tableau suivant donne les correspondances les plus utiles pour traduire une ligne de crontab en expression OnCalendar.
Besoin
Crontab
OnCalendar
Tous les jours à 2h00
0 2 * * *
*-*-* 02:00:00
Toutes les heures, à l'heure pile
0 * * * *
hourly ou *-*-* *:00:00
Toutes les 15 minutes
*/15 * * * *
*-*-* *:00/15:00
Du lundi au vendredi à 8h30
30 8 * * 1-5
Mon..Fri *-*-* 08:30:00
Le 1er de chaque mois à minuit
0 0 1 * *
monthly ou *-*-01 00:00:00
Chaque dimanche à 4h00
0 4 * * 0
Sun *-*-* 04:00:00
Le dernier jour du mois à 23h00
Impossible directement
*-*~01 23:00:00
Le 1er janvier à minuit
0 0 1 1 *
yearly
Au démarrage
@reboot
OnBootSec=
Deux remarques sur ce tableau :
Les raccourcis
minutely,hourly,daily,weekly,monthly,quarterly,semiannuallyetyearlysont acceptés tels quels. Attention,weeklycorrespond au lundi à minuit etdailyà minuit pileLa notation
~désigne un décompte depuis la fin du mois :*-*~01est le dernier jour du mois,*-02~03le troisième jour avant la fin de février. Cron n'a pas d'équivalent,.
Valider une expression avec systemd-analyze
C'est un avantage concret sur cron : vous pouvez vérifier une expression avant de l'utiliser, et systemd vous indique la prochaine occurrence. L'occasion de se faire la main sur la syntaxe de la planification avec Systemd.
systemd-analyze calendar "Mon..Fri *-*-* 08:30:00"Voici le résultat. Pratique, n'est-ce pas ?
L'option --iterations affiche les futures occurrences à venir (et successives), ce qui donne une idée plus précise du planning à venir. Surtout, si vous visez une exécution toutes les 15 minutes (par exemple), ceci permet de s'assurer que c'est bien le cas.
systemd-analyze calendar --iterations=5 "*-*-* *:00/15:00"Avec cron, une erreur de syntaxe se découvre souvent le lendemain, en constatant que la tâche n'a pas tourné. Ici, l'expression est vérifiée immédiatement.
Fuseau horaire
Par défaut, l'expression est interprétée dans le fuseau horaire de la machine. Il est possible de préciser un fuseau à la fin de l'expression, par exemple *-*-* 02:00:00 UTC ou *-*-* 02:00:00 Europe/Paris. C'est pratique pour un serveur configuré en UTC dont la tâche doit s'exécuter à une heure locale précise.
Les timers monotones
En plus des timers calendaires, systemd propose des timers relatifs à un événement, dans le même style que les événements proposés par le Planificateur de tâches de Windows. Ils sont indépendants de l'horloge système : un changement d'heure ou de fuseau ne les affecte pas.
Directive
Point de départ du délai
OnActiveSec=
Activation du timer lui-même
OnBootSec=
Démarrage de la machine
OnStartupSec=
Démarrage du gestionnaire systemd (utile pour les timers utilisateur)
OnUnitActiveSec=
Dernière activation du service déclenché
OnUnitInactiveSec=
Dernière désactivation du service déclenché
Le cas d'usage classique combine OnBootSec= et OnUnitActiveSec= pour obtenir une exécution périodique qui démarre peu après le boot :
[Timer]
# Première exécution 5 minutes après le démarrage
OnBootSec=5min
# Puis toutes les 30 minutes après la dernière exécution
OnUnitActiveSec=30minLes délais acceptent des unités lisibles : 30s, 5min, 2h, 1d, 1w.
Une subtilité à connaître : avec OnUnitActiveSec=, l'intervalle se mesure depuis le début de la dernière exécution, pas depuis sa fin. Si votre script tourne 10 minutes avec OnUnitActiveSec=30min, l'intervalle réel entre deux fins d'exécution reste 30 minutes, ce qui est le comportement attendu. En revanche, si le script dure plus longtemps que l'intervalle, le service sera relancé dès qu'il se termine.
Les options qui font la différence face à cron
Persistent : rattraper une exécution manquée
Avec cron, si la machine est éteinte ou qu'elle redémarre au moment où la tâche est prévue, celle-ci est tout simplement perdue (sauf à installer anacron). Avec Persistent=true, systemd enregistre sur disque la date de la dernière exécution. Au prochain démarrage du timer, si une exécution aurait dû avoir lieu pendant son inactivité, le service est lancé immédiatement.
[Timer]
OnCalendar=daily
Persistent=trueCette option ne concerne que les timers OnCalendar=. Elle est particulièrement adaptée aux machines qui ne tournent pas en permanence : un PC de bureau sous Debian, une VM de homelab que l'on éteint le soir, etc. Sur un serveur allumé en continu, elle reste utile pour couvrir les redémarrages dans le cadre des opérations de maintenance.
RandomizedDelaySec : étaler la charge
Sur un parc de machines qui exécutent la même tâche (mise à jour, sauvegarde vers un même serveur, synchronisation), tout déclencher à 2h00 pile crée un pic de charge sur le réseau ou sur le serveur cible. RandomizedDelaySec= ajoute un délai aléatoire, tiré entre 0 et la valeur indiquée, à chaque déclenchement. De quoi éviter que toutes les machines s'activent en même temps, surtout s'il y a un serveur en face.
[Timer]
OnCalendar=*-*-* 02:00:00
# Déclenchement entre 2h00 et 2h30
RandomizedDelaySec=30minC'est exactement ce que fait Debian avec apt-daily.timer, qui utilise un délai aléatoire pour ne pas saturer les miroirs. Avec cron, il faudrait ajouter un sleep $((RANDOM % 1800)) en tête de script, ce qui fonctionne mais reste une bidouille.
Note : pour que le délai reste identique d'une exécution à l'autre sur une même machine (tout en restant différent entre machines), ajoutez FixedRandomDelay=true. Le délai est alors dérivé de l'identifiant de la machine et du nom du timer.
AccuracySec : la précision par défaut n'est pas la seconde
Voici un piège classique quand on migre depuis cron. Par défaut, systemd s'autorise une fenêtre d'une minute (AccuracySec=1min) pour déclencher un timer, afin de regrouper les réveils du processeur et d'économiser de l'énergie. Un timer planifié à 02:00:00 peut donc se déclencher à 02:00:47. Et donc, potentiellement, vous pouvez penser que la tâche ne s'est pas lancée (si vous êtes devant l'écran en train de tester une nouvelle tâche).
Si votre tâche exige une exécution à la seconde près, réduisez cette valeur :
[Timer]
OnCalendar=*-*-* 02:00:00
AccuracySec=1sDeferReactivation : quand la tâche dure plus longtemps que l'intervalle
Comportement par défaut : si un service déclenché par OnCalendar= dure plus longtemps que l'intervalle entre deux occurrences, le timer relance le service immédiatement à la fin de l'exécution, car l'occurrence suivante est déjà dans le passé (ce que j'évoquais précédemment). Avec DeferReactivation=true, le timer attend la prochaine occurrence calendaire réelle après la fin du service.
[Timer]
OnCalendar=*-*-* *:00/10:00
DeferReactivation=trueAttention, cette directive a été introduite avec systemd 257 : elle est disponible sur Debian 13, mais pas sur Debian 12.
Consulter les journaux et déboguer un timer
La phrase suivante devrait vous plaire : tout ce que produit un service déclenché par un timer (sortie standard, sortie d'erreur, code de retour) est capturé par le journal de systemd. C'est intéressant pour la traçabilité des actions. Avec cron, la sortie part par e-mail via MAILTO si un agent de messagerie est configuré, ou disparaît dans /dev/null si vous l'y avez redirigée. A moins d'inclure une gestion de logs dans votre script ou outil lancé via cron.
journalctl -u backup-etc.serviceCi-dessous, un exemple :
Sep 22 18:14:02 SRV systemd[1]: Starting backup-etc.service - Sauvegarde du répertoire /etc...
Sep 22 18:14:02 SRV backup-etc.sh[56303]: tar: Removing leading `/' from member names
Sep 22 18:14:02 SRV backup-etc.sh[56301]: Sauvegarde terminée : /srv/backup/etc-20260922-181402.tar.gz
Sep 22 18:14:02 SRV systemd[1]: backup-etc.service: Deactivated successfully.
Sep 22 18:14:02 SRV systemd[1]: Finished backup-etc.service - Sauvegarde du répertoire /etc.En cas de problème, voici quelques pistes pour vous aider à dépanner :
Le timer n'apparaît pas dans
list-timers: vérifiez qu'il est activé et démarré (systemctl status backup-etc.timer). Un timerenablemais nonstartn'apparaît qu'avec--all. Un oubli dedaemon-reloadaprès édition est aussi une cause fréquente.Le service ne s'exécute pas à l'heure prévue : validez l'expression avec
systemd-analyze calendar, puis regardez la colonne NEXT delist-timers.Le service échoue : lancez-le à la main avec
systemctl startet lisezjournalctl -u.
Exécuter le service sous un utilisateur dédié
Par défaut, un service système s'exécute en root. Pour une tâche qui n'a pas besoin de ces privilèges, précisez un utilisateur et un groupe dans la section [Service] :
[Service]
Type=oneshot
User=backup
Group=backup
ExecStart=/usr/local/sbin/backup-etc.shC'est l'équivalent de la crontab d'un utilisateur ou de la colonne utilisateur dans /etc/crontab, avec un avantage : vous pouvez aussi appliquer les directives de durcissement de systemd (ProtectSystem=, PrivateTmp=, NoNewPrivileges=, etc.) pour limiter ce que le script peut faire. Cron n'offre rien de comparable.
Les timers utilisateur
Un utilisateur sans droits sudo peut aussi créer ses propres timers, à la manière de sa crontab personnelle. Ainsi, chaque utilisateur peut créer des unités dans ~/.config/systemd/user/ et se pilotent avec systemctl --user.
# Créer le répertoire des unités utilisateur
mkdir -p ~/.config/systemd/user
# Créer le service et le timer (même format que pour le système)
nano ~/.config/systemd/user/sync-notes.service
nano ~/.config/systemd/user/sync-notes.timer
# Recharger, activer et démarrer
systemctl --user daemon-reload
systemctl --user enable --now sync-notes.timer
# Lister les timers de l'utilisateur
systemctl --user list-timersLa dernière commande afficher uniquement les timers de l'utilisateur, et non ceux de root. Ainsi, dans cet exemple, il n'y a qu'une seule tâche :
NEXT LEFT LAST PASSED UNIT ACTIVATES
Tue 2026-09-22 18:36:00 UTC 1min 3s - - sync-notes.timer sync-notes.serviceUne différence importante avec la crontab utilisateur : par défaut, le gestionnaire systemd d'un utilisateur ne démarre qu'à l'ouverture de sa première session (console, SSH ou graphique) et s'arrête peu après la fermeture de sa dernière session (10 secondes par défaut). Ses timers ne tournent donc pas lorsqu'il n'est pas connecté. Pour qu'ils s'exécutent en permanence, comme le ferait cron, il faut activer le « lingering » : le gestionnaire de l'utilisateur est alors lancé au démarrage de la machine et maintenu après ses déconnexions.
sudo loginctl enable-linger nom_utilisateurPlanifier une tâche ponctuelle avec systemd-run
Vous avez besoin d'exécuter une tâche ponctuelle ou une seule fois à un moment bien spécifique. La flemme de créer un fichier timer et un service ! Il y a une solution pour ce cas de figur. En effet, pour une exécution unique ou un test rapide, il n'est pas nécessaire de créer des fichiers : systemd-run crée un timer transitoire, qui disparaît après exécution.
# Exécuter un script dans 30 minutes
sudo systemd-run --on-active=30min /usr/local/sbin/backup-etc.sh
# Exécuter à une heure précise
sudo systemd-run --on-calendar="2026-10-01 03:00:00" /usr/local/sbin/backup-etc.sh
# Lancer une unité existante plutôt qu'une commande
sudo systemd-run --on-active=10min --unit=backup-etc-test backup-etc.serviceTimer systemd ou cron : comparaison détaillée
Alors Timer systemd ou cron, lequel choisir ? Voici un tableau comparatif pour vous aider à y voir plus clair.
Critère
cron
Timer systemd
Configuration
Une ligne par tâche dans une crontab
Deux fichiers par tâche (.service + .timer)
Syntaxe horaire
5 champs, compacte, connue de tous
Expression calendaire lisible, plus verbeuse
Validation de la syntaxe
Aucune, erreur découverte à l'exécution
systemd-analyze calendar
Vue d'ensemble des tâches
Dispersée (crontabs utilisateurs, /etc/cron.d/, /etc/cron.*)
systemctl list-timers centralise tout
Journalisation
E-mail via MAILTO ou redirection manuelle
Automatique dans journalctl
Exécution manquée (machine éteinte)
Perdue (sauf anacron)
Rattrapée avec Persistent=true
Délai aléatoire
sleep $RANDOM dans le script
RandomizedDelaySec=
Exécutions concurrentes
Possibles, verrou flock à gérer
Impossible, une instance à la fois
Délai relatif au démarrage
@reboot uniquement
OnBootSec=, OnUnitActiveSec=, etc.
Dernier jour du mois
Test dans le script
*-*~01
Précision
À la minute (immédiat disons)
Fenêtre d'1 minute par défaut, réglable (AccuracySec=)
Isolation et sécurité
Aucune
Directives de durcissement systemd, cgroups
Dépendances
Aucune
Ordonnancement avec d'autres unités (After=, Requires=)
Test manuel
Copier-coller la commande
systemctl start dans le contexte exact
Portabilité
Toutes les distributions, y compris sans systemd
Distributions avec systemd uniquement
Quand préférer cron ?
Je ne vous dirai pas que cron est obsolète, ce serait faux. La preuve : vous l'utilisez surement encore aujourd'hui. Surtout, c'est plus rapide d'écrire une ligne de crontab plutôt que de monter une unité avec deux fichiers.
Une tâche triviale et jetable : pour un
curltoutes les 5 minutes sur une machine de test, une ligne de crontab est plus rapide à écrire que deux fichiers d'unitéLa portabilité : un script d'installation qui doit fonctionner sur Debian, Alpine, un BSD ou une vieille distribution sans systemd n'a d'autre choix que cron
Les habitudes d'équipe : si toute votre équipe lit une crontab d'un coup d'œil comme l'heure sur une montre et personne ne connaît systemd, imposer les timers a un coût
Les applications qui livrent leur propre
/etc/cron.d/: inutile de les réécrire, Debian continue de les honorer
Quand préférer les timers systemd ?
À l'inverse, je vous recommande les timers dès que l'un de ces critères est rempli :
La tâche a de l'importance : sauvegarde, rotation, synchronisation. Le rattrapage avec
Persistent=et les journaux centralisés changent la vie en cas d'incidentLa machine n'est pas allumée en continu
Vous gérez plusieurs machines :
RandomizedDelaySec=étale la charge sans bidouille notamment si c'est une action qui implique la connexion de X machines vers un même serveur.La tâche peut être longue : plus de risque d'exécutions en parallèle
Vous voulez restreindre les droits du script avec les directives de sécurité systemd
La tâche dépend d'un autre service (montage réseau, base de données), car vous pouvez l'exprimer avec
After=etRequires=dans le service
Migrer une tâche cron vers un timer systemd
Pour terminer, essayons de voir comment nous pouvons convertir une entrée cron existante (sans utiliser un outil en ligne). Prenons cette ligne d'une crontab root :
30 3 * * 0 /usr/local/sbin/clamav-scan.sh >> /var/log/clamav-scan.log 2>&1Il s'agit d'un scan antivirus hebdomadaire, comme celui que nous avions mis en place dans notre guide ClamAV sur Linux. La conversion se fait en quatre étapes.
Étape 1 : traduire l'horaire
Dans la syntaxe crontab 30 3 * * 0 signifie le dimanche à 3h30, soit Sun *-*-* 03:30:00. Un doute ? Vérifiez avec systemd-analyze :
systemd-analyze calendar "Sun *-*-* 03:30:00"Étape 2 : créer le service
La redirection vers un fichier de log devient inutile, le journal s'en charge.
# /etc/systemd/system/clamav-scan.service
[Unit]
Description=Scan antivirus hebdomadaire ClamAV
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/clamav-scan.shÉtape 3 : créer le timer
J'en profite pour ajouter le rattrapage en cas d'exécution loupée et un délai aléatoire.
# /etc/systemd/system/clamav-scan.timer
[Unit]
Description=Déclenchement hebdomadaire du scan ClamAV
[Timer]
OnCalendar=Sun *-*-* 03:30:00
Persistent=true
RandomizedDelaySec=15min
[Install]
WantedBy=timers.targetÉtape 4 : basculer
Activez le timer, puis seulement après, retirez la ligne de la crontab pour ne pas exécuter la tâche deux fois.
# Activer le timer
sudo systemctl daemon-reload
sudo systemctl enable --now clamav-scan.timer
# Vérifier
systemctl list-timers clamav-scan.timer
# Puis supprimer l'entrée cron (ligne à commenter, par exemple)
sudo crontab -eSi le script comptait sur des variables d'environnement définies dans la crontab (MAILTO, PATH, SHELL), reportez-les dans la section [Service] avec Environment=. Le cas de MAILTO mérite une petite précision pour conclure cet article en beauté : systemd n'envoie pas d'e-mail. Si vous avez besoin d'une notification en cas d'échec, la solution habituelle consiste à ajouter OnFailure=notify-echec@%n.service dans la section [Unit] du service, en pointant vers un service d'alerte que vous aurez créé (envoi d'un mail, d'un message via un webhook, etc.).
Conclusion
Les timers systemd ne sont pas une simple alternative à cron, ils corrigent ses limites structurelles : absence de journalisation intégrée, exécutions manquées perdues, exécutions concurrentes, aucune validation de syntaxe. Comme on l'a vu, on peut aller plus loin : en même temps, on pourrait presque dire qu'ils sont nés dans des époques différentes. En contrepartie, ils demandent un peu plus de rigueur (deux fichiers, la notion d'unité) et une syntaxe horaire moins compacte, mais plus lisible une fois le format assimilé.
Debian a d'ailleurs déjà tranché pour ses propres besoins : apt, logrotate, man-db ou fstrim sont pilotés par des timers. Rien ne vous oblige à migrer vos entrées cron existantes, mais désormais vous savez que les timers systemd sont une vraie solution.
Pour aller plus loin :
Linux : comment planifier des tâches avec cron et la crontab ? : notre tutoriel dédié à cron
Supervision de SystemD avec systemd-cgtop : pour suivre la consommation de ressources de vos services
Ajouter IT-Connect à mes
sources préférées
Cofondateur d'IT-Connect et Microsoft MVP "Cloud and Datacenter Management". Mon obsession depuis près de 15 ans ? Rendre l'administration système et la cybersécurité accessibles, que vous soyez junior ou confirmé. Plus qu'un métier, l'IT est pour moi une véritable passion. J’accompagne au quotidien les sysadmins et les professionnels de l’IT dans leur montée en compétences et leur veille technique.