Objectif
Planifier une tâche récurrente avec systemd en séparant clairement l’unité .service qui exécute l’action et l’unité .timer qui déclenche l’exécution, avec validation de la syntaxe, rattrapage éventuel après arrêt, journalisation native et possibilité de désactiver proprement l’automatisation.
Prérequis
- Distribution Linux utilisant systemd et accès sudo/root.
- Commande ou script déjà testé manuellement avec son utilisateur d’exécution.
- Horaire cible, politique de rattrapage après arrêt et tolérance de délai définis.
- Emplacement des fichiers ou données produits par la tâche et politique de rétention connue.
- Plan de retour consistant à désactiver le timer et à restaurer les anciens fichiers d’unité si nécessaire.
Procédure pas à pas
Inventorier l’automatisation existante
Vérifiez si une tâche cron, un ancien timer ou un service porte déjà le même objectif. Relevez l’utilisateur, la commande, les variables et les dépendances. Un doublon peut lancer la même opération deux fois et provoquer des traitements concurrents. Listez aussi les timers actifs afin de choisir un nom explicite qui ne collisionne pas avec une unité fournie par le système.
systemctl list-timers --all
crontab -l
sudo ls -la /etc/cron.d /etc/cron.daily 2>/dev/null
- L’automatisation actuelle et les éventuels doublons sont identifiés.
Créer et tester l’unité de service
Créez d’abord une unité .service de type oneshot. Utilisez un chemin absolu dans ExecStart et définissez User/Group si root n’est pas nécessaire. Si le script dépend d’un répertoire de travail ou d’un fichier d’environnement, déclarez-les explicitement. Ne mettez aucun secret en clair dans l’unité si un mécanisme plus sûr est disponible.
sudoedit /etc/systemd/system/boai-job.service
[Unit]
Description=BOAI job planifié
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=<USER>
Group=<GROUP>
WorkingDirectory=<WORKDIR>
ExecStart=/usr/local/sbin/<SCRIPT>
- Le fichier .service décrit une seule action reproductible avec les bons droits.
Recharger systemd et lancer le service manuellement
Après création ou modification d’une unité, rechargez la configuration puis exécutez le service sans timer. Vérifiez le code de sortie et le journal. Tant que cette exécution n’est pas propre, n’activez pas la planification : le timer ne ferait que répéter une erreur plus difficile à diagnostiquer.
sudo systemctl daemon-reload
sudo systemctl start boai-job.service
systemctl status boai-job.service --no-pager
journalctl -u boai-job.service -n 50 --no-pager
- Le service se termine avec un résultat attendu et les logs sont exploitables.
Créer l’unité timer
Créez le fichier .timer. OnCalendar convient aux horaires calendaires ; Persistent=true permet de rattraper une occurrence manquée pendant un arrêt. RandomizedDelaySec peut étaler des centaines de machines qui exécuteraient sinon la même tâche exactement à la même seconde. Déclarez WantedBy=timers.target pour permettre l’activation.
sudoedit /etc/systemd/system/boai-job.timer
[Unit]
Description=Déclenche BOAI job
[Timer]
OnCalendar=Mon..Fri *-*-* 03:15:00
Persistent=true
RandomizedDelaySec=10m
Unit=boai-job.service
[Install]
WantedBy=timers.target
- L’unité timer pointe explicitement vers le service prévu.
Valider l’expression de calendrier
Avant activation, demandez à systemd d’interpréter l’expression OnCalendar. Cette vérification évite les erreurs de jour, d’heure ou de syntaxe qui peuvent laisser une tâche silencieusement inactive. Comparez les prochaines occurrences avec le planning métier et le fuseau configuré sur le serveur.
systemd-analyze calendar 'Mon..Fri *-*-* 03:15:00'
timedatectl
- Les prochaines occurrences affichées correspondent au planning attendu.
Activer et démarrer le timer
Rechargez les unités puis activez le timer immédiatement. enable –now crée l’activation au démarrage et démarre le timer sans attendre un reboot. Vérifiez ensuite NEXT, LEFT, LAST et PASSED dans list-timers. Le service lui-même n’a généralement pas besoin d’être enabled lorsqu’il est déclenché uniquement par le timer.
sudo systemctl daemon-reload
sudo systemctl enable --now boai-job.timer
systemctl list-timers boai-job.timer --all
systemctl status boai-job.timer --no-pager
- Le timer est enabled/active et une prochaine occurrence cohérente est visible.
Forcer un test sans attendre l’horaire
Pour valider le chemin complet, déclenchez le service manuellement ou créez temporairement un horaire de test proche, puis revenez à la valeur définitive. Vérifiez que la sortie, les fichiers et l’action métier sont corrects. N’attendez pas le lendemain pour découvrir qu’un chemin relatif ou une permission diffère en exécution non interactive.
sudo systemctl start boai-job.service
journalctl -u boai-job.service --since '-10 min' --no-pager
- La même action fonctionne hors session interactive avec l’utilisateur prévu.
Contrôler les échecs et la journalisation
systemd journalise l’état du service et son code de sortie. Ajoutez une supervision si la tâche est critique : Failed, absence d’exécution récente ou fichier de sortie non généré. Pour un script bavard, configurez sa propre rotation ou limitez les logs ; ne laissez pas une tâche planifiée saturer /var/log ou le journal.
systemctl --failed
journalctl -u boai-job.service --since today
journalctl --disk-usage
- Un échec futur sera détectable sans connexion manuelle quotidienne.
Retirer l’ancien cron et documenter
Une fois plusieurs exécutions validées, supprimez ou commentez l’ancien mécanisme afin d’éviter les doublons. Documentez les deux fichiers d’unité, l’utilisateur, la fréquence, les dépendances et la méthode de test. Conservez la procédure de désactivation pour une maintenance ou un incident.
sudo systemctl disable --now boai-job.timer
sudo systemctl enable --now boai-job.timer
- Une seule source de planification reste active et la procédure est maintenable.
Validation
La procédure est validée lorsque :
- Le service oneshot fonctionne manuellement avec le même utilisateur que lors de l’exécution planifiée.
- systemd-analyze calendar et systemctl
- Le timer survit à un redémarrage et le comportement Persistent est conforme au besoin.
- Les logs journalctl permettent d’identifier le code de sortie et l’heure de chaque exécution.
Retour arrière
- Désactiver immédiatement le timer avec systemctl disable –now sans supprimer les logs.
- Restaurer l’ancien cron seulement si son comportement était connu et que le retour est nécessaire.
- Remettre les anciens fichiers .service/.timer sauvegardés puis exécuter systemctl daemon-reload.
- Supprimer les unités 2.6.29 uniquement après avoir confirmé qu’aucun autre service ne les référence.
Dépannage / erreurs fréquentes
- Timer active mais service jamais exécuté : vérifier Unit=, OnCalendar et systemctl list-timers –all.
- Service fonctionne en shell mais échoue via systemd : contrôler User, WorkingDirectory, chemins absolus et variables d’environnement.
- Exécution inattendue au démarrage : vérifier Persistent=true et la dernière occurrence manquée.
- Deux exécutions simultanées : rechercher cron ou autre timer encore actif et prévoir un verrou applicatif si la tâche peut dépasser son intervalle.
- Horaire décalé : contrôler timedatectl, fuseau et synchronisation de l’horloge.