Systemd Unit Validator · Scheduling
Vérifiez une unité systemd avant que systemd l’ignore.
Des fichiers .service, .timer et .socket, confrontés à la lecture qu’en fait systemd : la directive dans la mauvaise section, la faute de frappe qu’il jette sans le dire, le OnCalendar= qui signifie que votre timer ne se déclenchera jamais. Chaque constat nomme une ligne, dit ce que systemd en fait, et porte le correctif — et rien de ce que vous collez ne quitte l’onglet.
S’exécute dans votre navigateur — rien de ce que vous collez ne quitte cette page. Comment nous le prouvons
Playground du Systemd Unit Validator
WantedBy= lives in [Install] and is what systemctl enable reads to create its symlinks. Type= tells systemd how to decide the service is up. Persistent=true makes a calendar timer run immediately at boot if it missed its window.
Detected: —
Results update as you type — press Enter to run now.
Press Esc to release keyboard focus from the editor; ⌘/Ctrl + Enter checks and leaves the editor. Tap a Line badge to jump to that line. Nothing you paste is uploaded.
Paste a .service, .timer or .socket file above — or tap an example — to see line-numbered findings here, each with the reason it matters and the fix.
Le manque
systemd ne vous dit pas qu’il vous ignore.
Un fichier unit qui charge n’est pas un fichier unit qui fonctionne. Écrivez User=deploy dans [Unit] au lieu de [Service] et le service tourne en root ; écrivez WantedBy= là et systemctl enable ne fait silencieusement rien, si bien que l’unité ne revient pas après un redémarrage. Les deux journalisent une ligne — « Unknown key name … ignoring » — dans un journal que personne ne lit un bon jour, et les deux laissent une unité qui a l’air en bonne santé dans systemctl status.
L’outil de vérification livré par systemd, systemd-analyze verify, est plus complet que cette page ne le sera jamais — et il exige une machine avec systemd, le plus souvent root, et il résout votre unité contre les utilisateurs, les chemins et les drop-ins de CET hôte. Rien de tout cela n’est à votre disposition pendant la relecture d’un .service dans une pull request, sur un portable.
Et puis il y a la production générée. Demandez un timer à un assistant et vous obtenez quelque chose de fluide et plausible : RestartSecs=5 (cette directive n’existe pas), ExecReload= dans [Unit], un WantedBy= dans le mauvais bloc, ou le plus fréquent de tous : OnCalendar=*/15, de la syntaxe cron que systemd rejette en bloc, laissant un timer qui ne se déclenche jamais et n’apparaît jamais dans systemctl list-timers. Vérifier l’affirmation prend une seconde ; savoir quoi vérifier est la partie difficile. C’est à cela que servent les 35 vérifications d’ici.
Vous migrez depuis cron ? Le Convertisseur cron vers systemd écrit pour vous la paire .timer / .service — collez ensuite le résultat ici pour vérifier vos retouches à la main. Les deux outils partagent une seule grammaire OnCalendar= et ne peuvent donc pas se contredire sur un horaire.
Le pipeline
Comment ça marche.
Quatre étapes, toutes dans votre onglet, rejouées à chaque frappe.
-
Analyser comme systemd le fait.
Une continuation `\` se replie sur la ligne suivante — et une ligne de commentaire à l’intérieur de cette continuation est jetée, pas repliée. `#` et `;` n’ouvrent un commentaire qu’en début de ligne : il n’y a AUCUN commentaire de fin de ligne, donc `RestartSec=30 # plus tard` fixe vraiment la valeur à `30 # plus tard`.
-
Résoudre ce qui s’applique réellement.
Répéter une directive de liste ajoute ; répéter un scalaire signifie que le dernier gagne en silence ; une affectation vide remet la liste à zéro. Les règles lisent donc la valeur à laquelle systemd aboutit, pas la première du fichier — et disent quelle ligne a perdu.
-
Vérifier les noms, les valeurs et la sémantique.
Chaque nom est cherché dans la section où il est écrit, parmi 439 directives connues. Les valeurs ne sont vérifiées que là où systemd est strict : enums en respectant la casse, booléens sans la respecter, et OnCalendar= contre la grammaire complète de systemd.time(7).
-
Dire ce que systemd ferait.
Chaque constat nomme la ligne physique et cite la conséquence — « refuse de charger l’unité », « journalise Unknown key name et ignore la ligne ». Là où cette page ne peut pas savoir, elle le dit au lieu de deviner.
Référence
Quelle directive va où.
systemd ne lit une directive que dans la section qui la possède, et il compare les noms de section comme de directive en respectant la casse. Voici celles qui apparaissent dans de vraies unités — le validateur connaît 439 noms au total, répartis entre cinq sections qu’il vérifie et six qu’il nomme sans les vérifier.
[Unit]
| Directive | Ce qu’elle fait |
|---|---|
| Description= | Une ligne, affichée par systemctl status et dans les logs. |
| Documentation= | URIs séparées par des espaces — man:, https:, file:. |
| After= | Ordre seulement : démarrer après celles-ci, si elles démarrent aussi. |
| Wants= | Les entraîner, mais ne pas échouer si elles échouent. |
| Requires= | Les entraîner et échouer avec elles. L’ordre n’est PAS implicite. |
| ConditionPathExists= | Sauter l’unité (pas la faire échouer) si le chemin est absent. |
| StartLimitIntervalSec= | Fenêtre de la limite de démarrages. Ici, pas dans [Service]. |
| StartLimitBurst= | Démarrages permis dans cette fenêtre avant que systemd abandonne. |
[Install]
| Directive | Ce qu’elle fait |
|---|---|
| WantedBy= | Ce que `systemctl enable` relie. Pour un timer : timers.target. |
| RequiredBy= | Comme WantedBy=, mais la cible échoue si cette unité échoue. |
| Alias= | Nom supplémentaire auquel elle répond une fois activée. |
| Also= | Autres unités activées et désactivées avec celle-ci. |
[Service]
| Directive | Ce qu’elle fait |
|---|---|
| Type= | simple, exec, forking, oneshot, dbus, notify, notify-reload, idle. |
| ExecStart= | Chemin absolu. Une seule ligne sauf en Type=oneshot. Ce n’est pas un shell. |
| ExecStartPre= | S’exécute avant ExecStart= ; un échec annule le démarrage. |
| ExecReload= | Ce que lance `systemctl reload`. Ici, jamais dans [Unit]. |
| ExecStop= | Arrêt doux optionnel ; systemd envoie ensuite quand même les signaux de kill. |
| PIDFile= | En pratique obligatoire avec Type=forking : désigne le vrai processus principal. |
| Restart= | no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, always. |
| RestartSec= | Délai avant un redémarrage. PAS « RestartSecs ». |
| RemainAfterExit= | Déclarer un oneshot terminé comme actif plutôt que mort. |
| User= / Group= | Sous quelle identité tourne le processus. [Service], jamais [Unit]. |
| DynamicUser= | UID jetable à chaque démarrage. À associer à StateDirectory=. |
| StateDirectory= | systemd crée /var/lib/<nom> et en corrige le propriétaire à chaque démarrage. |
| WorkingDirectory= | Répertoire de travail. N’affecte pas la recherche des Exec*. |
| Environment= | Paires KEY=value ; répétable, et $KEY s’étend dans les lignes Exec*. |
| EnvironmentFile= | Lire les paires dans un fichier. Préfixé de `-`, l’absence est tolérée. |
| TimeoutStartSec= | Combien de temps systemd attend la fin du démarrage. |
[Timer]
| Directive | Ce qu’elle fait |
|---|---|
| OnCalendar= | Horaire à l’horloge, syntaxe systemd.time(7). Répétable. |
| OnBootSec= | Monotone : ce délai après le démarrage de la machine. |
| OnUnitActiveSec= | Monotone : ce délai après la dernière activation de l’unité. |
| AccuracySec= | Marge que systemd peut prendre pour regrouper les réveils. Par défaut 1min. |
| RandomizedDelaySec= | Étale le déclenchement pour éviter une ruée. |
| Persistent= | Timers de calendrier seulement : rattrape une exécution manquée machine éteinte. |
| Unit= | Ce qui est déclenché. Par défaut le .service de même nom de base. |
| WakeSystem= | Réveille la machine depuis la veille. Exige le matériel adéquat. |
[Socket]
| Directive | Ce qu’elle fait |
|---|---|
| ListenStream= | Port, adresse:port, ou chemin absolu pour une socket UNIX. |
| ListenDatagram= | L’équivalent UDP / SOCK_DGRAM. |
| Accept= | no : un service reçoit la socket d’écoute. yes : un par connexion. |
| SocketMode= | Permissions d’une socket UNIX ou d’un FIFO, p. ex. 0660. |
| SocketUser= / SocketGroup= | Propriétaire d’une socket UNIX dans le système de fichiers. |
| Service= | Ce qui est activé. Par défaut le .service de même nom de base. |
| FileDescriptorName= | Nomme le FD pour qu’un service à plusieurs sockets les distingue. |
Syntaxe de OnCalendar=
Le seul champ où une erreur reste invisible : un horaire que systemd ne sait pas analyser signifie que l’unité ne charge pas — le timer ne se déclenche donc jamais ET n’apparaît jamais comme cassé. Le validateur vérifie la grammaire complète de systemd.time(7) — le module même contre lequel écrit le Convertisseur cron vers systemd.
| Forme | Signification |
|---|---|
| DOW YYYY-MM-DD HH:MM:SS | La forme complète. Jour, date et secondes sont optionnels ; une date seule vaut 00:00:00. |
| * | N’importe quelle valeur pour ce composant. |
| a,b,c | Une liste de valeurs. |
| a..b | Un intervalle, plus petite valeur d’abord. Les jours vont de Mon à Sun, donc Sun ne peut pas ouvrir un intervalle. |
| a/n | Tous les n, à partir de a. La valeur de départ n’est PAS optionnelle — voir plus bas. |
| a..b/n | Tous les n sur un intervalle. |
| *-*~03 | Troisième jour avant la fin du mois (le `~` compte à rebours). |
| … UTC | … Europe/Paris | Un fuseau horaire final. Exige systemd 242 ou plus récent ; le nom est résolu contre la tzdata de l’hôte. |
| Raccourci | Équivaut à |
|---|---|
| minutely | *-*-* *:*:00 |
| hourly | *-*-* *:00:00 |
| daily | *-*-* 00:00:00 |
| monthly | *-*-01 00:00:00 |
| weekly | Mon *-*-* 00:00:00 |
| yearly | *-01-01 00:00:00 |
| annually | *-01-01 00:00:00 |
| quarterly | *-01,04,07,10-01 00:00:00 |
| semiannually | *-01,07-01 00:00:00 |
Rejeté — la répétition n’a pas de valeur de départ
[Timer]
OnCalendar=*/15
OnCalendar=0 3 * * * Accepté — toutes les 15 minutes, et 03:00 chaque jour
[Timer]
OnCalendar=*-*-* *:00/15:00
OnCalendar=*-*-* 03:00:00 La limite
Ce qu’il ne vérifie pas, volontairement.
Chacun de ces points a été envisagé puis écarté, et la même liste figure en commentaire au début du moteur. Un validateur qui devine serait pire qu’un validateur qui dit « je lis un fichier, comme du texte ».
Si les unités dont vous dépendez existent
After=, Wants= et Unit= nomment d’autres fichiers. Ils ne sont pas devant nous : un « unité introuvable » serait inventé.
Si un chemin, un utilisateur ou une capability existe
ExecStart=/usr/local/bin/x est vérifié comme absolu, pas comme présent. Le reste, seule votre machine le sait.
La syntaxe des durées
La grammaire de systemd pour `5min`, `1h30s` et `2 weeks` est généreuse. La réimplémenter produirait plus de faux positifs que de trouvailles.
Les drop-ins et l’accord avec le nom de fichier
Une surcharge .d/ et la question de savoir si le nom du fichier correspond à ce qu’attend Unit= sont hors d’un fichier collé seul.
L’ordre sans dépendance
After= sans Wants= est souvent délibéré. Le signaler serait une opinion déguisée en constat.
Les valeurs dans [Mount], [Path], [Swap], [Automount], [Slice] et [Scope]
Leurs NOMS de directives sont connus — `What=` dans [Service] est donc bien attribué — mais leurs valeurs ne sont pas vérifiées, et les résultats le disent explicitement.
ProtectSystem=, ProtectHome=, RestrictNamespaces= et compagnie
Elles acceptent des valeurs nommées OU un booléen : une vérification stricte rejetterait des entrées légales. Elles sont traitées comme des chaînes opaques, exprès.
PrivateTmp= comme simple booléen
systemd 257 a ajouté `PrivateTmp=disconnected`. Un validateur qui qualifierait d’erreur une valeur légale serait précisément le faux positif contre lequel cet outil existe.
Le prochain déclenchement d’un timer
Cela demande une horloge et une base de fuseaux horaires. Un « prochaine exécution » imprimé comme un fait et silencieusement faux est pire que pas de réponse.
Tout ce qui concerne le système en marche
Pas de systemctl, pas de journal, pas d’arborescence de drop-ins, pas de tzdata. Un fichier, lu comme du texte — c’est aussi pourquoi il ne demande pas root et n’envoie rien.
Limites annoncées plutôt que cachées : le validateur lit jusqu’à 200 000 caractères, garde au plus 20 constats par vérification et 200 au total, affiche au plus 50 lignes par sévérité, et vous donne le nombre réel dès qu’un plafond s’applique. Une directive non reconnue est toujours une note, jamais une erreur.
Étape suivante
Collez le rapport dans la revue.
« Copier le rapport » vous donne toute l’exécution en texte brut — une ligne par constat, avec le numéro de ligne, l’identifiant de la vérification et le correctif. Puis continuez : transformez une ligne de crontab en vrai timer, ou relisez d’abord un horaire cron en langage clair.
Systemd Unit Validator — 2 errors, 1 warning across 8 lines
Scope: system
ERRORS (2)
L3 wrong-section: WantedBy= belongs in [Install], not [Unit]. — fix: Move the line into the `[Install]` section.
L8 typo-directive: ExecStrat= is not a systemd directive — did you mean ExecStart=? — fix: Write `ExecStart=`.
WARNINGS (1)
missing-install: No [Install] section, so “systemctl enable” has nothing to do. — fix: Add `[Install]` with `WantedBy=multi-user.target` (or `default.target` for a user unit).
Checked client-side at opscanopy.com/systemd-unit-validator/ — 0 bytes uploaded. FAQ
Vos questions, nos réponses.
Appuyez sur une question pour afficher la réponse.
Que vérifie le Systemd Unit Validator ?
35 vérifications sur une véritable analyse du fichier, en quatre groupes. Structure : un en-tête de section invalide, un [unit] en minuscules, une section inconnue ou répétée, une ligne sans « = », une affectation écrite avant la première section. Noms : une directive dans la mauvaise section (User= dans [Unit]), une faute de frappe que systemd jette (ExecStrat=), un nom obsolète, un nom que la table ne connaît simplement pas. Valeurs : un enum incorrect — systemd les compare en respectant la casse —, un booléen qui n'en est pas un, un chemin Exec* non absolu, de la syntaxe shell dans une ligne Exec* que systemd n'interprétera pas, un spécificateur % inconnu, et un « commentaire » en fin de ligne qui fait en réalité partie de la valeur. Sémantique : un service sans ExecStart=, deux lignes ExecStart= quand Type n'est pas oneshot, Type=forking sans PIDFile=, Restart=always avec Type=oneshot, un timer sans déclencheur, un OnCalendar= que systemd ne sait pas analyser, Persistent= sur un timer monotone, un socket sans rien à écouter, un [Install] absent, et une cible système dans une unité utilisateur.
Est-ce systemd-analyze verify ?
Non, et cela ne peut pas l'être. systemd-analyze verify tourne sur une machine qui a systemd, demande root pour presque tout ce qui se trouve dans le chemin de recherche système et, surtout, résout votre unité contre CETTE machine : ses utilisateurs, ses chemins, ses drop-ins, ses autres unités. Cela en fait l'outil le plus complet — et le mauvais outil pour le moment où vous voulez vraiment la vérification : relire un .service dans une pull request sur un portable macOS, ou contrôler une unité qu'un assistant vient de vous écrire. Cette page lit un fichier comme du texte, dans votre navigateur, et énonce clairement la différence : tout ce qu'elle ne peut pas savoir à partir du seul texte est listé plus bas plutôt que deviné.
Mon fichier unit quitte-t-il un jour mon navigateur ?
Non. Le parseur et les 35 vérifications sont du JavaScript qui tourne dans votre onglet : pas de serveur, pas d'appel d'API, pas de journalisation, donc 0 octet est envoyé. Cela compte ici : les fichiers unit contiennent des noms d'hôtes internes, des chemins privés, des noms de comptes de service, des emplacements d'EnvironmentFile= et parfois un identifiant que personne n'aurait dû mettre dans une valeur.
Pourquoi mon timer systemd ne se déclenche-t-il jamais ?
La cause la plus fréquente, et de loin, est un OnCalendar= que systemd ne sait pas analyser, parce que de la syntaxe cron y a été collée. Deux formes expliquent presque tous les cas. D'abord un « */15 » nu : une répétition de calendrier systemd exige une valeur de départ explicite, donc « *-*-* *:00/15:00 » (ou sa forme courte « *:00/15:00 ») est accepté et « */15 » est rejeté en bloc. Ensuite un horaire crontab complet à cinq champs (« 0 3 * * * ») : les événements de calendrier systemd s'écrivent « DOW YYYY-MM-DD HH:MM:SS », pas en cinq champs, et cet outil reconnaît la forme et vous renvoie vers le convertisseur. Ensuite, cherchez un timer sans aucun déclencheur, et un .timer sans Unit= dont le .service porte un autre nom. Une unité que systemd refuse de charger n'apparaît jamais dans systemctl list-timers — d'où l'impression que tout va bien.
Pourquoi WantedBy= dans [Unit] est-il une erreur et non un avertissement ?
Parce que le réglage n'a aucun effet. systemd ne lit WantedBy= que dans [Install] ; ailleurs, il journalise « Unknown key name 'WantedBy' in section 'Unit', ignoring. » et passe à la suite. L'unité charge, démarre à la main, a l'air en bonne santé — et systemctl enable ne fait silencieusement rien, si bien qu'elle ne revient jamais après un redémarrage. Sur cette page, erreur signifie que systemd refuse l'unité OU écarte complètement le réglage ; avertissement signifie que le réglage s'applique et qu'il est un piège. Un réglage écarté appartient au premier groupe, car le fichier ne décrit plus ce que la machine va faire.
Pourquoi une directive non reconnue n’est-elle qu’une note ?
Parce que cette page embarque une table de directives — 439 noms — et que votre machine embarque un systemd. Chaque version apporte de nouvelles directives : un nom que cette table ignore peut être parfaitement valide là où vous allez l'exécuter. En faire une erreur vous entraînerait à ignorer le rapport entier, la seule chose à laquelle un linter ne survit pas. Un nom qui frôle une vraie directive, c'est autre chose : là, la preuve suffit pour affirmer que systemd jette la ligne, et CELA est signalé comme une erreur, avec la correction.
Que change le sélecteur Système / Utilisateur ?
Trois choses, toutes liées au gestionnaire qui chargera le fichier. En portée utilisateur, un WantedBy= qui nomme une cible système (multi-user.target, graphical.target) est signalé : le gestionnaire utilisateur n'a pas cette cible, donc systemctl --user enable n'a rien à lier et l'unité ne démarre jamais — default.target est ce qu'il faut, et timers.target existe bien là. La portée voyage aussi dans le lien de partage, pour qu'un lien envoyé reproduise les constats que vous avez vus, et pas d'autres. Tout le reste — l'analyse, les noms, les valeurs — est identique, car systemd lit le fichier de la même façon dans les deux cas.
Peut-il vérifier une unité modèle, un drop-in ou mes dépendances ?
Il lit exactement un fichier, donc : un modèle est analysé normalement et %i est signalé en note (il n'a de valeur que si le fichier s'appelle name@.service et est démarré en name@instance.service) ; un fragment drop-in s'analyse très bien seul, mais personne ici ne sait ce qu'il surcharge, donc un fichier partiel ressemblera à une unité sans ExecStart= ; et une unité nommée dans After=, Wants= ou Unit= n'est jamais résolue, car ce fichier n'est pas devant nous. Deviner dans ces trois cas donnerait une réponse assurée et fausse, ce qui est pire que pas de réponse.
Quelle taille de fichier accepte-t-il, et les constats sont-ils tronqués ?
Jusqu'à 200 000 caractères, soit environ quatre mille lignes : deux ordres de grandeur au-delà de n'importe quel fichier unit réel. Au-delà, il refuse avec un message plutôt que de figer votre onglet, car une entrée de cette taille est un extrait de journal, pas une unité. Les constats sont plafonnés aussi : 20 par vérification et 200 au total, et le panneau annonce le plafond et le nombre réel dès que l'un s'applique. La liste de résultats est plafonnée séparément à 50 lignes par sévérité, là encore avec le nombre réel.
More free, private DevOps tools.
Le Systemd Unit Validator est l’un des outils de OpsCanopy — une canopée grandissante de validateurs, convertisseurs et testeurs qui tournent dans le navigateur et ne touchent jamais un serveur.
More in Scheduling
39 free tools, every one offline-capable — opscanopy.com works with no signup and nothing uploaded.
À voir aussi : Convertisseur cron vers systemd pour écrire le timer au départ, Testeur d’expressions cron pour relire le côté cron en langage clair, le Dockerfile Linter et le GitLab CI Validator pour la même vérification « l’attraper avant la livraison » sur d’autres configurations, et le Env Example Checker pour les variables qu’un EnvironmentFile= est censé fournir — ou parcourez le répertoire complet des outils.
Sans affiliation ni approbation du projet systemd. Les noms de directives et de formats servent uniquement à décrire ce que cet outil vérifie ; confirmez toujours une unité auprès de la version de systemd qui va la charger.