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épendances

  • Le 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 cron

  • Timer 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

Schéma du fonctionnement d'un timer systemd qui déclenche un service à intervalle défini

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-timers

Sur 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 --all

Note : 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.sh

Contenu 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.sh

Testez 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.service

C'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.sh

Détaillons ces directives :

  • Description= : texte libre affiché par systemctl status et 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=simple qui 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.timer

Inté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.target

Ce 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 cible timers.target, ce qui permet de l'activer au démarrage avec systemctl 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.timer

Ce qui donne :

Sortie de systemctl list-timers montrant la prochaine exécution du timer systemd backup-etc

Pour tester sans attendre 2h00 du matin, lancez directement le service :

sudo systemctl start backup-etc.service

Puis, consultez le résultat :

systemctl status backup-etc.service

Voici 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:Seconde

Chaque 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, semiannually et yearly sont acceptés tels quels. Attention, weekly correspond au lundi à minuit et daily à minuit pile

  • La notation ~ désigne un décompte depuis la fin du mois : *-*~01 est le dernier jour du mois, *-02~03 le 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=30min

Les 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=true

Cette 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=30min

C'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=1s

DeferReactivation : 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=true

Attention, 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.service

Ci-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 timer enable mais non start n'apparaît qu'avec --all. Un oubli de daemon-reload aprè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 de list-timers.

  • Le service échoue : lancez-le à la main avec systemctl start et lisez journalctl -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.sh

C'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-timers

La 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.service

Une 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_utilisateur

Planifier 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.service

Timer 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

Tableau comparatif entre cron et les timers systemd sous Linux

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 curl toutes 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'incident

  • La 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= et Requires= 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>&1

Il 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 -e

Si 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 :

Ajouter IT-Connect à mes
sources préférées

author avatar

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.

readers loved this