# Cron vs daemon systemd — templates et arbre de décision Contexte CLAUDE.md pour choisir entre cron, systemd timer et daemon Node.js. Contient les templates de configuration commentés et l'arbre de décision. ## Section 1 : Arbre de décision Trois questions pour choisir : **1. La tâche doit-elle réagir à un événement en moins d'une minute ?** - Oui → **daemon** (cron et timer ne peuvent pas faire mieux qu'1 min) - Non → question suivante **2. A-t-elle besoin d'état entre les runs ?** - Oui → **daemon** (process continu avec état en mémoire) - Non → question suivante **3. Est-ce que les logs structurés ou la supervision crash comptent ?** - Oui → **systemd timer** (journald natif, restart, dépendances, sans event loop à écrire) - Non → **cron** (3 lignes de config, rien d'autre) Résumé : - Cron : tâche atomique périodique, simple, pas de logs critiques - systemd timer : tâche périodique + logs journald + supervision - Daemon : polling sub-minute, état en mémoire, réaction on-demand, file de jobs ## Section 2 : Template systemd .service (daemon Node.js) ```ini # ~/.config/systemd/user/mon-daemon.service [Unit] Description=Mon daemon Node.js After=network.target [Service] Type=simple # Répertoire de travail — tous les chemins relatifs dans le script partent d'ici WorkingDirectory=/home/USER/work/mon-projet # Commande de démarrage ExecStart=/usr/bin/node scripts/mon-daemon.js # Redémarrage automatique en cas de crash (pas sur arrêt volontaire) Restart=on-failure RestartSec=10 # Variables d'environnement (alternative : EnvironmentFile=.env) Environment=NODE_ENV=production # Logs dans journald automatiquement — pas besoin de redirection StandardOutput=journal StandardError=journal [Install] # Démarre avec la session utilisateur WantedBy=default.target ``` Commandes utiles : ```bash # Recharger la config systemd après modification du .service systemctl --user daemon-reload # Activer au démarrage systemctl --user enable mon-daemon # Démarrer maintenant systemctl --user start mon-daemon # Voir les logs en temps réel journalctl --user -u mon-daemon -f # Statut + dernières lignes de log systemctl --user status mon-daemon ``` Pour que les services utilisateur démarrent sans session ouverte (server headless) : ```bash loginctl enable-linger $USER ``` ## Section 3 : Template systemd timer (.service + .timer) ```ini # ~/.config/systemd/user/mon-job.service [Unit] Description=Mon job périodique After=network.target [Service] # oneshot = s'exécute et s'arrête (contrairement à simple qui tourne en continu) Type=oneshot WorkingDirectory=/home/USER/work/mon-projet ExecStart=/usr/bin/node scripts/mon-job.js ``` ```ini # ~/.config/systemd/user/mon-job.timer [Unit] Description=Déclenche mon-job.service périodiquement [Timer] # Délai après le démarrage du système (évite les déclenchements trop précoces au boot) OnBootSec=5min # Intervalle entre deux exécutions (à partir de la fin de la dernière) OnUnitActiveSec=6h # Alternatives : # OnCalendar=daily → tous les jours à minuit # OnCalendar=Mon *-*-* 9:00 → tous les lundis à 9h # OnCalendar=*:0/15 → toutes les 15 minutes # Rattraper les exécutions manquées (ex: machine éteinte pendant la fenêtre) Persistent=true [Install] WantedBy=timers.target ``` Commandes utiles : ```bash systemctl --user daemon-reload systemctl --user enable --now mon-job.timer # Voir tous les timers actifs et leur prochaine exécution systemctl --user list-timers # Forcer une exécution immédiate (sans attendre le timer) systemctl --user start mon-job.service ``` ## Section 4 : Template crontab ```bash # Éditer avec : crontab -e # Vérifier avec : crontab -l # Syntaxe : minute heure jour-du-mois mois jour-de-la-semaine commande # * = tous # */n = tous les n # 1,3,5 = valeurs spécifiques # Publier sur dev.to à 9h tous les jours 0 9 * * * bash /home/USER/work/cv/scripts/devto-cron.sh >> /home/USER/work/cv/logs/devto-cron.log 2>&1 # Publier sur LinkedIn à 10h tous les jours 0 10 * * * /usr/bin/node /home/USER/work/cv/scripts/linkedin-cron.js >> /home/USER/work/cv/logs/linkedin-cron.log 2>&1 # Toutes les 15 minutes (avec flock pour éviter l'overlap) */15 * * * * flock -n /tmp/mon-job.lock /usr/bin/node /home/USER/scripts/mon-job.js >> /tmp/mon-job.log 2>&1 # Variables d'environnement disponibles dans crontab SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin # Charger un fichier .env avant le script (bash requis) 0 9 * * * bash -c 'set -a; source /home/USER/work/cv/scripts/.mon-env; set +a; node /home/USER/work/cv/scripts/mon-script.js' >> /home/USER/logs/mon-script.log 2>&1 ``` Notes importantes : - Le cron tourne dans un environnement minimal — pas de PATH, PS1, ou variables de session - Toujours utiliser des chemins absolus (`/usr/bin/node`, pas `node`) - `2>&1` redirige stderr vers stdout (dans le même fichier log) - `flock -n` échoue silencieusement si le lock est déjà pris (évite l'overlap) ## Section 5 : Commandes de debug ```bash # --- DAEMON systemd --- # Logs en temps réel journalctl --user -u mon-daemon -f # Logs des dernières 100 lignes journalctl --user -u mon-daemon -n 100 # Logs depuis une date journalctl --user -u mon-daemon --since "2026-03-25 10:00" # Statut complet (state, PID, dernières lignes) systemctl --user status mon-daemon # Redémarrer systemctl --user restart mon-daemon # --- TIMER systemd --- # Voir tous les timers et leur prochaine exécution systemctl --user list-timers # Forcer exécution immédiate sans attendre le timer systemctl --user start mon-job.service # Logs du dernier run journalctl --user -u mon-job.service -n 50 # --- CRON --- # Voir les jobs en cours d'exécution crontab -l # Logs du cron système (selon la distro) journalctl -u cron -f # ou tail -f /var/log/syslog | grep CRON # Tester un script cron dans les mêmes conditions (env minimal) env -i HOME=$HOME /bin/bash --noprofile --norc -c 'source /home/USER/.bashrc; mon-script.sh' # --- OVERLAP (flock) --- # Vérifier si un lock est actif flock -n /tmp/mon-job.lock echo "libre" || echo "déjà en cours" # Voir quel process tient le lock lsof /tmp/mon-job.lock ```