Waarom wordt mijn cron-taak niet uitgevoerd?
Ontdek de vijf meest voorkomende redenen waarom een cron-taak faalt. Wij behandelen het ontbrekende PATH, niet-geëscapete procenttekens en scripts die een login-shell vereisen.
Waarom uw cron-taak niet wordt uitgevoerd
Een cron-taak die "nooit wordt uitgevoerd", is bijna altijd wel uitgevoerd. Deze werd uitgevoerd in een omgeving die niet uw shell is, faalde in de eerste seconde, en het bericht werd verzonden naar een locatie die u niet controleert. Vijf oorzaken verklaren vrijwel elke melding: het zoekpad, het procentteken, het verkeerde crontab-bestand, uitvoer die naar mail is verzonden, en een script dat een inlogsessie verwacht.
cron is een daemon (een achtergrondservice) die crontab-bestanden leest en opdrachten volgens een schema start. Het leest niet uw .bashrc, het opent geen terminal, het start geen login-shell en het meldt u niet wanneer een opdracht faalt. Elke onderstaande oorzaak vloeit voort uit deze vier feiten.
Doorloop ze in volgorde en begin met de vraag die aan alles ten grondslag ligt: is cron überhaupt gestart? "cron heeft de taak nooit gestart" en "de taak is gestart en direct beëindigd" zijn verschillende problemen die niets met elkaar gemeen hebben, dus beantwoord die vraag eerst.
Is cron überhaupt uitgevoerd?
De daemon heeft verschillende unit-namen, afhankelijk van de distributiefamilie. Controleer beide en lees vervolgens het logbestand.
systemctl status cron
systemctl status crond
journalctl -u cron --since "2 hours ago"
journalctl -u crond --since "2 hours ago"Debian en Ubuntu noemen de unit cron. Fedora, Rocky en Alma noemen deze crond. Slechts één van deze namen bestaat op een specifieke machine; het is dus normaal en geen fout als een van de twee commando's meldt dat de unit onbekend is.
Lees de vermeldingen die uw eigen systeem heeft geschreven. Zoek niet naar een regel die uit een handleiding is gekopieerd, omdat de bewoording verschilt tussen cron-implementaties en log-configuraties. U controleert slechts twee zaken: staat er een vermelding op de minuut die in uw planning staat, en bevat die vermelding uw commando? Een vermelding met uw commando betekent dat cron zijn taak heeft uitgevoerd en dat de fout zich in het commando zelf bevindt. Geen enkele vermelding betekent dat cron uw planning nooit heeft ontvangen; dit is oorzaak 3 hieronder.
Sommige images sturen cron-berichten via rsyslog naar een bestand in plaats van naar de journal. Kijk in /var/log naar een bestand dat vernoemd is naar cron of syslog en lees het einde daarvan.
ls -l /var/log
sudo tail -n 50 /var/log/syslogAls noch de unit, noch het logbestand bestaat, is cron mogelijk niet geïnstalleerd. Minimale cloud-images en containers laten dit vaak weg.
dpkg -l cron
rpm -q cronie
sudo apt install cron
sudo dnf install cronie
sudo systemctl enable --now cronOorzaak 1: cron beschikt niet over uw PATH
Uw interactieve shell bouwt PATH op vanuit /etc/profile, ~/.profile, ~/.bashrc en alles wat deze bestanden inladen. Niets hiervan wordt uitgevoerd voor een cron-taak. cron start het commando met zijn eigen beperkte omgeving, waardoor een programma dat zich buiten de standaard systeemmappen bevindt niet wordt gevonden. Alles onder /usr/local/bin, /opt, een taalversiebeheerder, een Python virtual environment of een Go workspace is hiervoor gevoelig. De taak faalt bij de eerste regel en de shell schrijft een "not found"-foutmelding waarvan de exacte bewoording afhangt van de gebruikte shell.
Zoek het werkelijke pad van elk commando dat uw taak gebruikt.
command -v docker
command -v node
readlink -f "$(command -v node)"Schrijf vervolgens deze absolute paden in de taak, of stel PATH eenmalig in bovenaan de crontab.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * /usr/local/bin/mytool runHaal die lijst op van uw eigen machine met echo "$PATH" en verwijder alles wat alleen bestaat binnen een interactieve sessie. Eén regel is hierbij van belang: cron breidt variabelen in deze toewijzingsregels niet uit. PATH=$PATH:/usr/local/bin slaat de letterlijke tekst $PATH:/usr/local/bin op, waardoor de taak eindigt met een zoekpad dat geen enkele bruikbare map bevat. Schrijf de volledige lijst uit.
Een versiebeheerder heeft meer nodig dan alleen een pad. nvm, pyenv, rbenv en asdf installeren een shell-functie of een shims-map vanuit uw .bashrc, en een cron-taak leest dat bestand nooit. Roep het versie-specifieke binaire bestand aan via het absolute pad, of laad het init-script van de beheerder als de eerste regel van uw eigen script.
Oorzaak 2: het procentteken beëindigt uw commando
In het commando-veld van een crontab is % geen gewoon teken. Het eerste niet-geëscapete % beëindigt het commando. Alles wat daarna komt, wordt als standaardinvoer aan het commando doorgegeven, en elk volgend % wordt een nieuwe regel. Dit is een echte cron-functie voor het invoeren van korte gegevens aan een programma, en het is ook de reden waarom een bestandsnaam met een datumstempel het klassieke voorbeeld is van een defecte crontab-regel.
Schrijf 0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +%F).tar.gz /srv/site en tar ziet nooit een geformatteerde datum. cron knipt de regel af bij het eerste %, waardoor de shell een onvoltooide command substitution ontvangt en de rest van uw regel als standaardinvoer binnenkomt. Escap elk procentteken met een backslash.
0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +\%F).tar.gz /srv/siteTwee lagen lezen die ene regel, in volgorde. \% is een cron-regel, toegepast door cron voordat het iets start. $(date +\%F) is command substitution, later toegepast door de shell die cron start. Weten welke laag welk teken beheert, is de hele truc.
De veiligere gewoonte is om logica volledig buiten de crontab te houden. Plaats deze in een script, waar het procentteken geen speciale betekenis heeft.
#!/bin/bash
set -euo pipefail
stamp="$(date +%F)"
tar -czf "/srv/backups/site-${stamp}.tar.gz" /srv/siteDe crontab-regel bevat dan alleen een pad en een redirect en niets anders. Een crontab die u in één oogopslag kunt lezen, is een crontab die u kunt debuggen.
Oorzaak 3: welke crontab heeft u bewerkt?
Er is niet één enkele crontab. Er zijn meerdere bestanden, met verschillende eigenaren en een verschillend aantal velden; een taak die in het verkeerde bestand staat, is onzichtbaar.
crontab -ebewerkt de crontab van de gebruiker die het commando uitvoert.sudo crontab -ebewerkt die van root. Twee personen die op dezelfde machine debuggen, lezen vaak twee verschillende bestanden.sudo crontab -l -u deploytoont de crontab van een andere gebruiker; dit is de manier om te bevestigen wat er daadwerkelijk is geïnstalleerd voor het account dat de taak moet uitvoeren./etc/crontaben elk bestand in/etc/cron.dbevatten één extra veld tussen de planning en het commando: de gebruiker als wie de taak moet draaien. Plak een crontab-regel met vijf velden in/etc/cron.den het eerste woord van uw commando wordt gelezen als een gebruikersnaam.- Bestanden in
/etc/cron.dmoeten namen hebben die bestaan uit letters, cijfers, underscores en koppeltekens. Een bestand genaamdbackup.shofsite.confwordt alleen al vanwege de naam overgeslagen. Hernoem het naarbackupen controleer uw log opnieuw. - Bestanden in
/etc/cron.dmoeten eigendom zijn van root en mogen niet schrijfbaar zijn door de groep of anderen.ls -l /etc/cron.dtoont u beide feiten in één keer. - Scripts die in
/etc/cron.dailyen de bijbehorende mappen worden geplaatst, volgen dezelfde naamgevingsregel en moeten bovendien het uitvoerbare bit (execute bit) hebben. Een ontbrekend uitvoerbaar bit leidt tot het geruisloos overslaan van het script. /etc/cron.allowen/etc/cron.denybepalen wie überhaupt een crontab mag installeren. Als een van beide op uw systeem bestaat, lees deze dan voordat u ervan uitgaat dat uw gebruiker er een mag aanmaken.
Installeer een gebruikers-crontab met het commando crontab in plaats van het spool-bestand handmatig te bewerken, omdat crontab het bestand valideert voordat het wordt geïnstalleerd. Lees bij het opslaan wat het commando teruggeeft. Als het bestand wordt geweigerd, blijft de vorige versie actief en is uw wijziging nooit doorgevoerd; dit ziet er precies zo uit alsof cron u negeert.
De eigenaar bepaalt ook de rechten. Een taak in de crontab van root maakt bestanden aan die eigendom zijn van root, waar de applicatie die ze moet lezen mogelijk niet naar kan schrijven. Een taak in de crontab van een normale gebruiker kan geen directory lezen die alleen toegankelijk is voor root. Stem de eigenaar af op het werk: onderhoud aan applicaties hoort bij het eigen account van de applicatie, wat de reden is achter het vervangen van WordPress wp-cron door een systeem-cronjob. De modus van de bestanden die uw taak aanmaakt, komt voort uit de umask die wordt geërfd, en dat is een andere waarde dan die van uw shell, dus hoe umask bestandsrechten instelt is het lezen waard als de output van een taak onleesbaar op de schijf belandt.
Oorzaak 4: de uitvoer werd naar e-mail gestuurd die niemand leest
cron verzamelt alles wat een taak naar de standaarduitvoer en standaardfout schrijft. Als de taak ook maar iets heeft geschreven, stuurt cron die tekst naar het lokale e-mailsysteem, geadresseerd aan de eigenaar van de crontab of aan wat MAILTO aangeeft. Op een uitgeklede VPS is meestal geen MTA (mail transfer agent) geïnstalleerd, dus wordt er niets afgeleverd. Uw foutmelding bestond even en verdween vervolgens in het niets. Dat is precies de reden waarom een defecte taak geruisloos lijkt te falen.
Stuur de uitvoer in plaats daarvan naar een bestand dat u zelf beheert.
0 3 * * * /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1>> voegt de standaarduitvoer toe aan het bestand. 2>&1 wijst de standaardfout naar de huidige bestemming van de standaarduitvoer, dus dit moet na de redirect komen. Als u dit andersom schrijft, als 2>&1 >> file, behoudt de standaardfout zijn oorspronkelijke bestemming en is de fout die u probeert te vinden precies het deel dat het bestand nooit bereikt.
Het journal is het andere goede doel. logger schrijft naar syslog onder een tag die u zelf kiest.
0 3 * * * /usr/local/sbin/backup-site.sh 2>&1 | logger -t backup-siteLees het terug met journalctl -t backup-site. Hierdoor blijft de uitvoer van de taak zelf bij de cron-regels staan, waardoor de tijdlijn eenvoudig te volgen is. Als u ook een overzicht nodig heeft van welke gebruiker welk commando op de server heeft uitgevoerd, dan is dat een apart systeem, en het auditen van gebruikerscommando's op uw server behandelt dit.
MAILTO="" bovenaan een crontab schakelt e-mail uit voor de taken daaronder. Het instellen van MAILTO op een geldig adres helpt alleen als er een werkende MTA aanwezig is; controleer dus eerst of e-mail de server daadwerkelijk verlaat voordat u hierop vertrouwt.
Eén regel tijdens het debuggen: voeg nooit > /dev/null 2>&1 toe. Dit is de meest gebruikte regel in elke crontab, en het gooit het enige bewijsmateriaal dat u heeft weg. Voeg het later weer toe als u dat wilt, zodra de taak naar behoren werkt.
Oorzaak 5: het script gaat uit van een omgeving die cron niet biedt
Zodra het commando is gevonden en de uitvoer is vastgelegd, blijft al het overige over dat uw sessie u normaal gesproken automatisch aanbiedt.
- De shell is mogelijk niet bash. Controleer dit met
ls -l /bin/sh. Op Debian en Ubuntu verwijst dit naar dash, waardoor de dubbele haakjes-test, arrays ensourcefalen met een syntaxfout. Voorzie het script van een#!/bin/bash-regel en roep het script aan, of stelSHELLin bovenaan de crontab. - De werkmap is niet de map waarin u zich bevond. Gebruik overal absolute paden, of gebruik
cdnaar de map op de eerste regel van het script. Een relatief pad is de meest voorkomende reden waarom een taak "werkt wanneer ik het handmatig uitvoer". - De locale is niet die van uw sessie. Alles wat een datum of getal opmaakt, of tekst sorteert, kan andere uitvoer produceren onder een andere
LANG. Als een latere stap die uitvoer verwerkt, stel de locale dan in het script in in plaats van te hopen op de juiste instellingen. - Er is geen TTY (terminal). Een commando dat om bevestiging vraagt, een editor opent of een voortgangsbalk tekent, kan blijven hangen of afsluiten. Voeg de non-interactive flag toe die het hulpprogramma biedt.
- Er is geen SSH-agent.
SSH_AUTH_SOCKbevindt zich niet in de omgeving van cron, dus eenssh- ofrsync-commando dat werkte omdat uw agent was geladen, faalt nu bij de authenticatie. Geef de taak een eigen sleutel, waarvan de eigenaar de gebruiker van de taak is. - Er is geen gebruikerssessiebus, dus
systemctl --uservanuit een cron-job faalt totdatXDG_RUNTIME_DIRis ingesteld. Een system unit is hiervoor de betere oplossing.
Op Fedora, Rocky en Alma is er nog een verdachte. SELinux beperkt cron-jobs, waardoor een taak die een pad met een onverwacht label benadert, wordt geweigerd, zelfs als de bestandsrechten correct lijken. Controleer op weigeringen met sudo ausearch -m avc -ts recent en lees SELinux-basisprincipes voor een server voordat u iets uitschakelt.
De test van één minuut die de omgeving van cron toont
Stop met gissen naar de inhoud van de omgeving van cron en lees deze uit. Schrijf een script dat alles dumpt, plan dit elke minuut in, wacht even en lees vervolgens het bestand.
cat > /home/deploy/cron-probe.sh <<'EOF'
#!/bin/bash
echo "=== probe ==="
date -Is
pwd
id
echo "SHELL=$SHELL"
echo "LANG=$LANG"
command -v node || echo "node is not on this PATH"
env | sort
EOF
chmod +x /home/deploy/cron-probe.shVoeg één regel toe aan de crontab van de gebruiker onder wiens account de eigenlijke taak wordt uitgevoerd, waarbij u aan beide kanten absolute paden gebruikt.
* * * * * /home/deploy/cron-probe.sh >> /home/deploy/cron-probe.log 2>&1Wacht een minuut, lees daarna /home/deploy/cron-probe.log en vergelijk dit met dezelfde commando's uitgevoerd in uw eigen shell. De regel PATH, de werkmap en de locale verklaren het falen meestal direct. Let op twee details van de configuratie: de procenttekens staan in het script, waar de regel van cron niet van toepassing is, en het logpad is een locatie waar de gebruiker van de taak naar kan schrijven.
Verwijder die crontab-regel zodra u het antwoord heeft. Een taak die elke minuut draait en gegevens toevoegt aan een bestand, zal een kleine schijf vullen, en dit gebeurt geruisloos.
Is dit de planning die u bedoelde?
Een regel in een user crontab begint met vijf velden: minuut, uur, dag van de maand, maand, dag van de week. Twee van deze velden werken op een manier die vaak voor verrassingen zorgt.
Wanneer zowel de dag van de maand als de dag van de week zijn beperkt, wat betekent dat geen van beide * is, voert cron de taak uit wanneer één van beide velden overeenkomt. 0 0 13 * 5 betekent niet "vrijdag de 13e". Het wordt uitgevoerd om middernacht op de 13e van elke maand, en om middernacht op elke vrijdag. Om één specifieke dag te krijgen, laat u een van de twee velden op * staan en test u de andere binnen het script.
cron gebruikt de tijdzone van het systeem. Veel VPS-images worden geleverd met de instelling op UTC (Coordinated Universal Time), dus een taak die u voor 03:00 hebt gepland, wordt uitgevoerd om 03:00 UTC, wat voor u midden in de middag kan zijn. timedatectl toont welke tijdzone uw server daadwerkelijk gebruikt. Controleer uw eigen instelling in plaats van aan te nemen dat deze overeenkomt met die van uw laptop.
Er zijn nog twee valkuilen bij het plannen die het vermelden waard zijn. @reboot wordt geactiveerd wanneer cron zelf start, wat niet hetzelfde moment is als het moment waarop het netwerk gereed is. Een taak die DNS of een externe host nodig heeft, kan daarom bij het opstarten mislukken, terwijl deze bij elke handmatige uitvoering daarna wel slaagt. Bovendien is er niets dat voorkomt dat een trage taak opnieuw start terwijl het vorige exemplaar nog steeds wordt uitgevoerd. Beveilig deze taak met een lock.
*/5 * * * * /usr/bin/flock -n /tmp/backup-site.lock /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1flock -n stopt onmiddellijk wanneer de lock al in gebruik is, zodat de overlappende uitvoering wordt afgebroken in plaats van dat deze zich opstapelt boven op de eerste taak.
Wanneer een systemd timer de betere keuze is
cron is goed in één ding: een commando uitvoeren op een specifiek tijdstip. Op alle andere vlakken schiet het tekort. Een timer biedt u de journal zonder dat u output hoeft om te leiden, een exit-status die u later kunt opvragen, volgordelijkheid ten opzichte van network-online.target, en een willekeurige vertraging zodat honderd servers niet allemaal op exact dezelfde seconde starten. Wanneer uw taak een van deze functies vereist, is een systemd service en timer op een VPS minder bewerkelijk dan het onderhouden van een crontab-regel. Retry-gedrag hoort hier ook thuis, omdat systemd restart policies bepalen wat er na een fout gebeurt, terwijl cron voor die vraag helemaal geen oplossing biedt.
Gebruik cron voor kleine taken. Verplaats alles met afhankelijkheden of een retry-beleid naar een timer. Beide kunnen op dezelfde server draaien, dus dit is geen migratie die u in één keer hoeft af te ronden.
FAQ
Waarom werkt mijn cron-taak handmatig wel, maar niet via cron?
Omdat uw shell en de omgeving van cron verschillen. Uw inlog-shell leest /etc/profile en ~/.bashrc, waarin PATH, de locale en uw agent-variabelen worden ingesteld. cron start het commando zonder deze instellingen, vanuit een andere werkmap en soms met een andere shell. Gebruik absolute paden voor elk commando, stel de benodigde variabelen in aan het begin van de crontab of in het script zelf, en plan een testtaak van één minuut in die env | sort, pwd en id naar een logbestand schrijft. Zo kunt u de werkelijke omgeving van cron inzien in plaats van te gissen.
Hoe controleer ik of cron mijn taak daadwerkelijk heeft uitgevoerd?
Lees het logbestand van de daemon. Gebruik journalctl -u cron op Debian en Ubuntu, of journalctl -u crond op Fedora, Rocky en Alma; sommige images sturen de berichten via rsyslog door naar een bestand onder /var/log. Zoek naar een vermelding op de minuut die in uw schema staat en controleer of het commando daar wordt genoemd. Geen vermelding betekent dat cron het schema niet heeft ontvangen; controleer dus of u de juiste crontab heeft bewerkt. Een vermelding zonder resultaat betekent dat het commando is gestart en direct is beëindigd; vang de uitvoer daarom op met een redirect.
Waarom werkt date +%Y niet in een crontab?
cron behandelt % als een speciaal teken in het commando-veld. Het eerste niet-geëscapte % beëindigt het commando; alles wat daarna komt, wordt als standaardinvoer naar dat commando gestuurd, en elk volgend % wordt een nieuwe regel. Hierdoor bereikt een bestandsnaam met datumopmaak nooit het programma waarvoor u deze heeft geschreven. Escap elk procentteken als \%, of verplaats het commando naar een script en roep dat script aan vanuit cron, aangezien het procentteken binnen een script geen speciale betekenis heeft.
Waar gaat de uitvoer van mijn cron-taak naartoe?
Naar het lokale mailsysteem, geadresseerd aan de eigenaar van de crontab of aan de gebruiker die in MAILTO is opgegeven. De meeste VPS-images hebben geen mail transfer agent geïnstalleerd, waardoor het bericht verloren gaat en de taak geruisloos lijkt te falen. Stuur de uitvoer naar een bestand met >> /path/to/log 2>&1, waarbij u deze volgorde aanhoudt zodat de standaardfoutuitvoer de standaarduitvoer volgt, of pipe de uitvoer door logger -t myjob en lees deze uit met journalctl -t myjob. Gebruik > /dev/null 2>&1 niet zolang u nog aan het debuggen bent.
Moet ik cron of een systemd timer gebruiken?
Gebruik cron voor een eenvoudig commando op een vast tijdstip, zeker als u de taak mogelijk moet verplaatsen naar een machine die geen systemd draait. Gebruik een timer wanneer u de uitvoer in de journal wilt zien zonder redirects, een opvraagbare exit-status nodig heeft, de taak wilt laten starten nadat het netwerk actief is, een willekeurige startvertraging wilt instellen of een retry-beleid bij fouten wilt toepassen. Beide kunnen op dezelfde server naast elkaar draaien, zodat u taken één voor één kunt migreren zodra dat nodig is.