Créer un cron job : planification, droits et erreurs les plus fréquentes
La tâche fonctionne dans le shell, mais pas dans cron. Ce guide explique les cinq champs de temps, la différence entre crontab utilisateur et /etc/cron.d, le piège du PATH et comment prouver qu'une exécution a réellement réussi.
Un cron job se configure en cinq minutes, puis il coûte souvent des heures. La commande fonctionne parfaitement dans le shell, rien ne se passe depuis cron, et le journal indique au mieux que cron a démarré quelque chose. Cet article traite précisément des points où les tutoriels habituels s'arrêtent : les vrais messages d'erreur, les différences entre les distributions et la question de savoir comment vérifier qu'une tâche est réellement allée jusqu'au bout, et pas seulement qu'elle a démarré.
Toutes les indications se rapportent à Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS et Ubuntu 22.04 LTS. Ces quatre systèmes utilisent le même cron (la variante Debian de Vixie Cron), pas cronie comme Red Hat et Fedora. Cette différence sera déterminante plus loin, au chapitre du fuseau horaire.
Le service cron tourne-t-il seulement ?
Sur une installation complète, cron est présent. Dans les images cloud minimales, les images de base de conteneurs (container) et les installations netinst allégées, le paquet manque régulièrement. C'est la première question à trancher avant de déboguer quoi que ce soit.
command -v crontab
systemctl status cron
Volontairement command -v crontab et non command -v cron : le service lui-même se trouve dans /usr/sbin, et ce répertoire n'apparaît sur Debian que dans le chemin de recherche de root. En tant qu'utilisateur normal, vous n'obtenez tout simplement aucune sortie, alors même que cron est installé. Sous Ubuntu 24.04, la requête fonctionne aussi pour un utilisateur non privilégié. crontab, en revanche, se trouve partout dans /usr/bin et reste donc visible pour tout le monde, et systemctl status cron répond de toute façon à la question la plus importante : le service tourne-t-il vraiment ?
Si le service manque, installez-le :
apt-get update
apt-get install -y cron
systemctl enable --now cron
Sur Debian et Ubuntu, le service s'appelle cron, pas crond. Qui a pris l'habitude de Red Hat obtient ici un Unit crond.service could not be found. et cherche au mauvais endroit.
En fonctionnement normal, aucun redémarrage n'est nécessaire : le cron de Debian surveille les répertoires de crontab via inotify et relit les modifications de lui-même. Après avoir modifié une crontab, vous n'avez donc rien à redémarrer. Les exceptions sont le changement de fuseau horaire système et les modifications de /etc/default/cron.
Les cinq champs, et le piège du cinquième
Chaque ligne commence par cinq champs de temps, la commande suit ensuite.
| Champ | Plage | Remarque |
| Minute | 0 à 59 | |
| Heure | 0 à 23 | format 24 heures, aucun fuseau horaire |
| Jour du mois | 1 à 31 | |
| Mois | 1 à 12 | également jan à dec |
| Jour de la semaine | 0 à 7 | 0 et 7 valent tous deux dimanche, également sun à sat |
30 4 * * * /usr/local/bin/backup.sh # tous les jours à 04:30
*/10 * * * * /usr/local/bin/check.sh # toutes les 10 minutes
0 2 * * 0 /usr/local/bin/weekly.sh # le dimanche à 02:00
15 3 1 * * /usr/local/bin/monthly.sh # le 1er du mois à 03:15
0 9-17 * * 1-5 /usr/local/bin/business.sh # en semaine, toutes les heures de 9 à 17
Deux détails sont presque toujours mal compris.
Le jour du mois et le jour de la semaine forment un OU, pas un ET. Dès que les deux champs sont restreints, c'est-à-dire qu'aucun ne contient d'astérisque, la tâche s'exécute si l'un des deux correspond. 0 3 13 * 5 ne signifie pas « vendredi 13 », mais « tous les 13 du mois et, en plus, tous les vendredis ». Si vous voulez vraiment le vendredi 13, vérifiez-le dans le script lui-même.
Les pas ne divisent pas de façon régulière. */7 * * * * s'exécute aux minutes 0, 7, 14, 21, 28, 35, 42, 49 et 56, puis le compteur passe à l'heure suivante. Entre 56 et 0, il ne reste donc que quatre minutes. Cela vaut pour tout intervalle qui ne divise pas exactement 60, respectivement 24. Pour « toutes les 90 minutes », cron n'offre aucune écriture propre : un timer systemd est ici le meilleur choix.
Utiliser crontab -e correctement
Ne modifiez jamais la crontab utilisateur directement dans /var/spool/cron/crontabs/, passez toujours par l'outil :
crontab -e
Au premier appel, le programme répond no crontab for root - using an empty one. Sur un système fraîchement installé, un choix d'éditeur suit. Si aucun éditeur n'est présent, par exemple dans une image allégée, l'appel s'interrompt avec un message du type /usr/bin/sensible-editor: 25: editor: not found. Remède : installer un éditeur ou définir explicitement celui que vous voulez.
apt-get install -y nano
EDITOR=nano crontab -e
Le grand avantage de crontab -e par rapport à l'écriture directe du fichier est la vérification à l'enregistrement. Si une ligne est cassée, vous voyez :
"/tmp/crontab.7hK2mn/crontab":3: bad minute
errors in crontab file, can't install.
Do you want to retry the same edit? (y/n)
Le numéro de ligne est juste, le champ nommé dans le message ne l'est pas toujours : bad minute apparaît aussi lorsqu'il manque simplement un champ, car l'analyseur lit alors tout décalé vers la gauche. En cas de succès, l'opération se termine par crontab: installing new crontab. Seule cette ligne signifie que la modification a été reprise.
D'autres commandes que vous devriez connaître :
crontab -l # afficher
crontab -l > /root/crontab.bak # sauvegarder
crontab -u www-data -l # lire la crontab d'un autre (en root)
crontab -i -r # supprimer, avec confirmation (au terminal)
Une remarque sur la sauvegarde : crontab -l > /root/crontab.bak renvoie no crontab for root et un code de retour 1 tant qu'aucune crontab n'existe encore pour l'utilisateur. Le fichier cible est tout de même créé, mais vide, 0 octet. Dans l'ordre de lecture de cet article, cela passe inaperçu ; dans un script qui teste le code de retour ou qui écrase une sauvegarde plus ancienne, beaucoup moins.
crontab -r sans -i supprime la crontab complète immédiatement et sans demander confirmation. Comme r et e ne sont pas très éloignées sur le clavier, il s'agit d'un cas réel de perte de données. Prenez donc l'habitude d'utiliser crontab -i -r au terminal, et sauvegardez d'abord avec crontab -l.
Le mot important ici est terminal. crontab -i -r est une commande purement interactive. Si l'entrée standard n'est pas rattachée à un terminal, donc dans un script, dans un cron job ou dans une commande d'une seule ligne comme ssh host "crontab -i -r", la demande de confirmation tourne en boucle sans fin : crontab: really delete root's crontab? (y/n) Please enter Y or N: Please enter Y or N: ... se répète indéfiniment et produit en quelques secondes des centaines de kilo-octets de sortie ; l'appel ne se termine plus que par une interruption venue de l'extérieur. Pour tout ce qui est automatisé, utilisez donc la variante non interactive et sauvegardez avant :
crontab -l > /root/crontab.bak # sauvegarder avant de supprimer
crontab -r # supprimer sans confirmation
printf "y\n" | crontab -i -r # garder la question, fournir la réponse
Si /etc/cron.allow ou /etc/cron.deny existent, ce sont eux qui décident qui a le droit de créer des crontabs. Les utilisateurs concernés obtiennent : You (username) are not allowed to use this program (crontab). Si /etc/cron.allow est présent, il s'applique de manière exclusive : tout utilisateur non listé est bloqué.
Crontab utilisateur, /etc/crontab et /etc/cron.d
Il existe quatre endroits où des plannings peuvent se trouver, et leurs formats diffèrent. C'est précisément là que naissent les erreurs les plus difficiles à trouver.
Crontab utilisateur : cinq champs
Maintenue via crontab -e, elle s'exécute sous l'utilisateur auquel elle appartient et ne comporte aucun champ utilisateur. Si vous en ajoutez un malgré tout, cron tente d'exécuter le nom d'utilisateur comme une commande et vous trouvez dans le mail ou dans le journal :
/bin/sh: 1: root: not found
/etc/cron.d : six champs
Les fichiers de /etc/cron.d/ sont des crontabs système et comportent un champ utilisateur entre les champs de temps et la commande. C'est le bon endroit pour les tâches qui appartiennent à une application ou à une gestion de configuration, car chaque fichier peut être remplacé individuellement.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""
0 3 * * * root /usr/local/bin/backup.sh >> /var/log/kh-backup.log 2>&1
Si vous oubliez le champ utilisateur, cron interprète le premier mot de la commande comme un nom d'utilisateur et la ligne échoue en silence, parce que cet utilisateur n'existe pas.
Trois règles concernant /etc/cron.d/ sont régulièrement enfreintes :
- Le nom de fichier ne doit contenir aucun point. Seuls les lettres, les chiffres, les tirets bas et les traits d'union sont autorisés.
backup.cronoukh-backup.shsont ignorés sans le moindre commentaire. La raison tient à la gestion des paquets : les restes comme.dpkg-distou.dpkg-oldne sont ainsi jamais exécutés. Nommez simplement le fichierkh-backup. - Les droits et le propriétaire doivent être corrects. Sont attendus
root:rootet le mode 0644. Sinon, le journal reçoit des messages comme(*system*) WRONG FILE OWNER,(*system*) BAD FILE MODEou une remarque sur un mode non sûr, accessible en écriture au groupe ou aux autres. La tâche ne s'exécute alors pas. - Le fichier doit se terminer par un saut de ligne. cron exige que chaque entrée se termine par un newline. Si la dernière ligne n'a pas de saut de ligne final, elle est ignorée, et cela sans le moindre commentaire : lors d'un contre-test avec un fichier par ailleurs identique mais sans saut de ligne final, la tâche ne s'est pas exécutée une seule fois, sans message d'erreur et sans ligne de journal. Si vous générez le fichier avec
echo -n, avec unprintfsans\nfinal ou depuis un modèle sans ligne vide à la fin, vous perdez précisément la dernière tâche.
chown root:root /etc/cron.d/kh-backup
chmod 0644 /etc/cron.d/kh-backup
/etc/crontab et les répertoires cron.*
/etc/crontab compte également six champs et appartient à la distribution. Ne le modifiez que si vous savez pourquoi. Via run-parts, il appelle les répertoires /etc/cron.hourly, cron.daily, cron.weekly et cron.monthly. Les scripts qui s'y trouvent ont besoin du bit d'exécution et ne doivent pas non plus contenir de point dans leur nom. Un backup.sh dans /etc/cron.daily/ ne sera jamais exécuté, un backup oui. Vous pouvez tester cela sans rien exécuter :
run-parts --test /etc/cron.daily
Seuls les scripts que run-parts démarrerait réellement sont affichés. Si le vôtre manque dans la liste, la cause est le nom ou le bit d'exécution.
Pourquoi le PATH est différent dans cron
C'est de loin la cause la plus fréquente du « ça marche dans le shell, mais pas dans cron ». cron ne démarre pas de shell de connexion. Ni ~/.bashrc, ni ~/.profile, ni /etc/profile ne sont lus. Pour les crontabs utilisateur, le cron de Debian définit un chemin de recherche minimal :
PATH=/usr/bin:/bin
/usr/local/bin et /usr/sbin manquent donc. Sur Debian 12, Debian 13, Ubuntu 22.04 et Ubuntu 24.04, /usr/bin et /usr/sbin restent des répertoires distincts, le usr-merge ne concerne que /bin et /sbin. Tout ce que vous avez placé vous-même dans /usr/local/bin, tout ce qui vient de pip install, tout ce qui vient d'un Node installé par nvm et les outils système comme ufw ou iptables n'existent tout simplement pas pour cron. Le message d'erreur est alors :
/bin/sh: 1: backup.sh: not found
La deuxième partie du piège : cron définit SHELL=/bin/sh. Sur Debian et Ubuntu, /bin/sh est un lien vers dash, pas vers bash. Toute construction propre à bash, dans l'en-tête du script ou directement dans la ligne de crontab, échoue :
/bin/sh: 1: [[: not found
/bin/sh: 1: source: not found
/bin/sh: 1: Syntax error: "(" unexpected
Trois contre-mesures, dans cet ordre :
- Utilisez des chemins absolus.
/usr/local/bin/backup.shau lieu debackup.sh,/usr/bin/phpau lieu dephp. C'est la variante la plus robuste, car elle fonctionne indépendamment de toute variable d'environnement. Relevez cependant le chemin au lieu de le taper de mémoire :command -v daterenvoie/usr/bin/datesur Debian 13, Debian 12, Ubuntu 24.04 et Ubuntu 22.04, mais/bin/datesur des systèmes plus anciens comme Debian 11. Une ligne au mauvais chemin est acceptée par crontab sans commentaire et sans avertissement, elle n'échoue qu'à l'exécution, et en silence. Pour contrôler, lancez une foiscrontab -let une foisenv -i /bin/sh -c "/usr/bin/date": un mauvais chemin est signalé immédiatement parnot found. - Définissez PATH et SHELL en tête de la crontab. Les affectations valent pour toutes les lignes suivantes. Important : cron n'y développe aucune variable,
PATH=$PATH:/opt/binne fonctionne pas, écrivez le chemin en entier. - Utilisez un script wrapper. La ligne de crontab n'appelle que le script, et le script met en place son propre environnement.
#!/bin/bash
set -euo pipefail
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cd /srv/app
exec ./do-the-work.sh
Si vous voulez savoir ce que cron transmet réellement à votre tâche, faites-vous le montrer. Ajoutez pour une minute :
* * * * * /usr/bin/env > /tmp/cron-env.txt 2>&1
Seule cette approche montre l'environnement que cron met réellement en place. Un shell reconstitué avec env -i s'en approche seulement, car il apporte son propre chemin par défaut. Comparez ensuite /tmp/cron-env.txt avec votre environnement interactif. Ce qui saute aux yeux, ce ne sont en général pas seulement PATH et SHELL, mais aussi les variables de locale absentes. Sans LANG, tout tourne dans la locale C, ce qui modifie les ordres de tri, les formats de date et l'affichage des caractères accentués. Les scripts qui reposent sur LANG=fr_FR.UTF-8 se comportent différemment sous cron. Le ssh-agent manque lui aussi, raison pour laquelle les tâches qui accèdent en SSH ont besoin d'une clé sans phrase de passe et d'un -i explicite.
La dernière petite méchanceté de cette catégorie : le signe pourcent. Dans une ligne de crontab, un % non échappé est transformé en saut de ligne, et tout ce qui suit part vers la commande comme entrée standard. Les formats de date doivent donc être échappés.
0 2 * * * /usr/bin/tar -czf /backup/web-$(date +\%F).tar.gz /var/www
Rediriger la sortie, les mails et « No MTA installed »
Par défaut, cron envoie par mail au propriétaire de la crontab tout ce qu'une tâche écrit sur stdout ou stderr. Sur un serveur sans système de messagerie, cela finit dans le journal :
(CRON) info (No MTA installed, discarding output)
Ce n'est pas une erreur de la tâche. Cela signifie seulement qu'une sortie a été produite et que personne n'a pu la réceptionner. Une tâche silencieuse ne génère pas cette ligne. Elle constitue donc même un signal utile : si elle apparaît soudainement, c'est que votre tâche s'est mise à produire quelque chose, le plus souvent un message d'erreur.
Pour la redirection, il existe trois modèles pertinents :
# tout dans un fichier de journal, erreurs comprises
0 3 * * * /usr/local/bin/backup.sh >> /var/log/kh-backup.log 2>&1
# sortie normale supprimée, erreurs toujours par mail
0 3 * * * /usr/local/bin/backup.sh > /dev/null
# tout dans le journal, proprement étiqueté
0 3 * * * /usr/local/bin/backup.sh 2>&1 | /usr/bin/logger -t kh-backup
La variante > /dev/null 2>&1 est appréciée et dangereuse : elle jette aussi tous les messages d'erreur. Une tâche qui échoue depuis quatre mois ressemble alors exactement à une tâche qui fonctionne. Si vous voulez vous passer des mails, préférez MAILTO="" en tête de la crontab et écrivez la sortie dans un fichier. Si vous voulez des mails vers une adresse précise, mettez MAILTO=alerts@example.org, mais il vous faut alors un système de messagerie installé.
Vos propres fichiers de journal dans /var/log/ grossissent sans limite. Créez pour cela une petite règle dans /etc/logrotate.d/, sinon le cron job qui fonctionne le mieux finira par remplir le disque.
Comment savoir que la tâche a vraiment tourné
C'est ici que le tutoriel rapide se sépare de la vérification solide. cron journalise le démarrage d'une tâche. Il ne journalise ni la fin ni le code de sortie. Une ligne CMD dans le journal prouve donc seulement que cron a démarré le shell, pas que votre script a réussi.
La lecture des journaux diffère selon le système :
journalctl -u cron --since "30 min ago"
journalctl -t CRON --since today
Sur Debian 12 et Debian 13, c'est la seule voie possible, car depuis Bookworm rsyslog n'est plus installé par défaut. Un fichier /var/log/syslog n'y existe en règle générale plus. Qui le cherche sans le trouver croit à tort que la tâche n'a pas démarré.
Sur Ubuntu 22.04 et 24.04, rsyslog est habituellement présent, et vous pouvez en plus utiliser :
grep CRON /var/log/syslog
Aucune des quatre versions ne fournit d'origine un /var/log/cron.log dédié. La règle correspondante dans /etc/rsyslog.d/50-default.conf est commentée. Ne cherchez pas ce fichier, il n'existe que si quelqu'un l'a activé.
Une preuve propre vient donc de la tâche elle-même. Faites écrire au script, à la fin, un horodatage et le code de sortie :
#!/bin/bash
set -euo pipefail
trap 'echo "$(date -Is) kh-backup terminé, exit $?" >> /var/log/kh-backup.log' EXIT
# le travail proprement dit
Vous disposez ainsi de trois preuves au lieu d'une : la ligne CRON dans le journal (cron a démarré), la ligne de fin dans votre fichier de journal (le script est allé jusqu'au bout) et le code de sortie (il s'est terminé proprement). La tâche ne fonctionne que lorsque les trois concordent.
Si une tâche peut durer plus longtemps que son intervalle, protégez-la en plus contre le chevauchement. Sinon, dix instances finiront par démarrer en parallèle et saturer le serveur :
*/5 * * * * /usr/bin/flock -n /var/lock/kh-sync.lock /usr/local/bin/sync.sh
flock -n se termine immédiatement si une instance tourne déjà. L'outil fait partie de util-linux et est présent sur les quatre systèmes.
@reboot et pourquoi systemd vaut mieux la plupart du temps
@reboot permet d'exécuter une commande au démarrage. Il existe à côté @daily, @hourly, @weekly, @monthly et @yearly, qui remplacent chacun les cinq champs de temps.
@reboot /usr/local/bin/start-app.sh
Cela paraît pratique et présente trois faiblesses sérieuses :
- Le moment n'est pas « après le boot », mais « quand cron démarre ». Savoir si le réseau, la base de données ou un montage sont prêts à cet instant relève du pur hasard. Le dépannage habituel consiste à placer un
sleep 30devant, ce qui ne fait que déplacer le problème. - Un redémarrage du service cron déclenche @reboot une nouvelle fois. Un
systemctl restart cron, par exemple après une mise à jour de paquets, démarre votre application une deuxième fois alors que la première tourne encore. - Il n'y a aucune surveillance. Pas de code de sortie, pas de redémarrage en cas de plantage, pas de statut.
Pour tout ce qui doit tourner en permanence, il faut un service systemd, pas un cron job. Pour les tâches récurrentes, un timer est le meilleur choix. Deux fichiers suffisent :
[Unit]
Description=Sauvegarde KernelHost
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
[Unit]
Description=Sauvegarde KernelHost quotidienne
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=900
[Install]
WantedBy=timers.target
C'est le timer que l'on active, pas le service :
systemctl daemon-reload
systemctl enable --now kh-backup.timer
systemctl list-timers kh-backup.timer
Les trois avantages décisifs par rapport à cron : Persistent=true rattrape au démarrage suivant une exécution manquée, alors que cron la saute sans un mot. Le code de sortie apparaît dans systemctl status kh-backup.service, vous voyez donc sans journalisation maison si cela a marché. Et la sortie complète est disponible avec journalctl -u kh-backup.service, sans redirection et sans mail. Le chemin de recherche est d'ailleurs plus généreux chez systemd que chez cron et contient par défaut aussi /usr/local/bin, mais sans intervention de votre part il reste malgré tout limité.
Pour les tâches de démarrage, remplacez @reboot par un service aux dépendances claires :
[Unit]
After=network-online.target
Wants=network-online.target
Votre tâche ne s'exécute ainsi qu'une fois le réseau réellement en place, c'est garanti. Vous trouverez davantage d'informations sur les units et leur structure dans créer un service systemd.
Fuseau horaire, UTC et heure d'été
cron calcule toujours dans le fuseau horaire système issu de /etc/localtime. Sur beaucoup de serveurs et dans presque toutes les images cloud, c'est UTC. Une tâche planifiée à 0 3 * * * s'exécute alors en été à 05:00, heure d'été d'Europe centrale, et non à 03:00. Vérifiez d'abord à quoi vous avez affaire :
readlink -f /etc/localtime
timedatectl show -p Timezone --value
date
date -u
readlink -f /etc/localtime indique le chemin dans /usr/share/zoneinfo, et donc le fuseau réellement en vigueur, par exemple /usr/share/zoneinfo/Etc/UTC ou /usr/share/zoneinfo/Europe/Vienna. La commande fonctionne sur les quatre versions, y compris là où systemd ne tourne pas. timedatectl show -p Timezone --value livre la même information de façon courte et lisible par une machine, mais elle suppose systemd.
Ce dont il faut perdre l'habitude, c'est de regarder dans /etc/timezone. Debian 13 ne livre plus ce fichier, un cat /etc/timezone s'y termine par cat: /etc/timezone: No such file or directory, et même une installation de tzdata ne le fait pas revenir. Sur Debian 12, Ubuntu 24.04 et Ubuntu 22.04, il existe encore, la réponse varie donc selon la version. Dans tous les cas, seul le lien symbolique /etc/localtime fait foi : une valeur écrite à la main dans /etc/timezone ne change rien au fuseau horaire système, et donc rien non plus au moment de déclenchement de vos tâches.
Vous pouvez changer le fuseau horaire système à tout moment ; redémarrez ensuite cron pour que le service reprenne la modification à coup sûr :
timedatectl set-timezone Europe/Vienna
systemctl restart cron
Sur les systèmes où systemd ne tourne pas, définissez plutôt le lien symbolique directement : le résultat est identique et se contrôle aussitôt avec readlink -f /etc/localtime :
ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
Voici maintenant le point que beaucoup de tutoriels donnent faux : le cron de Debian et d'Ubuntu ne connaît pas CRON_TZ. Cette variable vient de cronie, le cron de Red Hat, Fedora et AlmaLinux. Sur Debian 13, Debian 12, Ubuntu 24.04 et Ubuntu 22.04, il n'existe pas de fuseau horaire par utilisateur ni par crontab. Si vous y placez TZ=Europe/Vienna dans la crontab, cela agit uniquement sur l'environnement des commandes exécutées : un date dans le script affiche donc l'heure de Vienne. Le moment de déclenchement lui-même n'en est absolument pas affecté et continue de suivre le fuseau horaire système. Qui passe à côté de cela se retrouve avec une tâche apparemment bien configurée qui tourne pourtant deux heures à côté.
Il reste trois voies propres : régler correctement le fuseau horaire système, convertir vous-même les heures en UTC, ou passer à un timer systemd, qui accepte un fuseau horaire directement dans le planning. La vérification se fait sans risque :
systemd-analyze calendar "Mon..Fri 03:00 Europe/Vienna"
La sortie indique le prochain déclenchement sous forme de date concrète. C'est le contrôle le plus fiable que vous puissiez faire avant l'activation.
Un conseil pratique au sujet de l'heure d'été : ne planifiez aucune tâche entre 02:00 et 03:00. En mars, cette heure n'existe pas, en octobre elle existe deux fois. Selon la tâche, cela signifie une exécution manquée ou une exécution en double, les deux une fois par an et les deux difficiles à reproduire. 01:30 ou 03:30 sont des créneaux de remplacement discrets. Avec les timers systemd, Persistent=true aide en plus à rattraper une exécution manquée.
Diagnostic rapide en sept étapes
Si un cron job ne s'exécute pas, parcourez cette liste dans l'ordre. Elle couvre pratiquement tous les cas.
- Le service tourne-t-il ?
systemctl status cron. Pas de service, pas de tâche. - L'entrée est-elle bien arrivée ?
crontab -lpour les tâches utilisateur, sinon regardez le fichier dans/etc/cron.d/. Contrôlez le nom de fichier sans point, le mode 0644, le propriétaire root et le saut de ligne final. - cron l'a-t-il seulement démarrée ?
journalctl -t CRON --since today. Si la ligne CMD manque, le problème vient du planning ou du fichier, pas du script. - Le nombre de champs est-il correct ? Cinq champs dans la crontab utilisateur, six dans
/etc/cron.det/etc/crontab. - Chemins absolus ? Inscrivez chaque commande et chaque script avec son chemin complet, et déterminez ce chemin au préalable avec
command -vau lieu de le taper. - Environnement vérifié ? Ajoutez une seule fois
* * * * * /usr/bin/env > /tmp/cron-env.txt 2>&1, attendez une minute, comparez. - Tester dans le contexte de cron. N'exécutez pas le script dans votre shell, mais avec un environnement vide :
env -i PATH=/usr/bin:/bin /bin/sh -c '/usr/local/bin/backup.sh'. Vous indiquez ici le chemin de recherche à dessein.env -i /bin/shseul vide certes l'environnement, mais dash se donne ensuite son propre chemin par défaut/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, plus généreux que celui de cron. Précisément les erreurs dont il est question ici resteraient ainsi invisibles. Si l'appel échoue, vous avez trouvé la cause, sans avoir à attendre le prochain moment de déclenchement.
Le dernier point est le plus précieux. Presque tous les cron jobs qui ne fonctionnent pas « sans explication » échouent de façon reproductible dès qu'on les démarre avec un environnement vide. Une énigme qui ne se manifeste que toutes les 24 heures devient ainsi une erreur que vous corrigez en dix secondes.
Si vous lancez régulièrement des sauvegardes par cron job, sécurisez également la machine cible. Vous trouverez les indications correspondantes dans notre article sur la sécurisation d'un serveur Linux.
Questions fréquentes
Pourquoi mon script fonctionne-t-il dans le shell mais pas en cron job ?
Que signifie le message No MTA installed, discarding output ?
Debian ou Ubuntu prennent-ils en charge la variable CRON_TZ ?
Pourquoi mon fichier dans /etc/cron.d est-il ignoré ?
Où trouver les journaux de cron sur Debian 12 et 13 ?
Une ligne CMD dans le journal prouve-t-elle que la tâche a réussi ?
Faut-il utiliser @reboot ou un service systemd ?
2026 KernelHost GmbH. Tous droits réservés. Ce guide est protégé par le droit d'auteur. Sa republication sur d'autres sites web, même partielle ou sous une forme modifiée, n'est pas autorisée sans notre accord écrit. Les citations accompagnées de la source et d'un lien sont expressément les bienvenues.

