Cheat Sheet Crontab: 50+ espressioni cron, sintassi e guida agli scheduler moderni
Un’espressione cron ha cinque campi (minuto, ora, giorno del mese, mese, giorno della settimana) seguiti da un comando. Questa grammatica guida lo scheduling Unix dal 1979 e oggi muove anche i Kubernetes CronJob, GitHub Actions, AWS EventBridge e i cron trigger di Vercel. Imparala una volta, pianifica ovunque.
Se sviluppi e ti serve un’espressione adesso (un task Linux, un Kubernetes CronJob, un trigger di GitHub Actions, oppure devi capire perché un job ogni cinque minuti scatta solo ogni ora), scorri fino alla tabella di riferimento rapido per espressioni pronte da copiare, salta a Sintassi decodificata per le regole dei campi, oppure apri il Generatore Crontab — un’alternativa a crontab guru privacy-first che gira nel tuo browser — per validare le espressioni in tempo reale.
Tabella di riferimento rapido per espressioni cron
Trenta espressioni che coprono circa il 90% delle reali esigenze di scheduling. Ognuna è un cron POSIX a cinque campi valido: incollala in crontab -e, in uno schedule: di Kubernetes oppure in un cron: di GitHub Actions.
| Pianificazione | Espressione cron | In parole semplici |
|---|---|---|
| Ogni minuto | * * * * * | ogni minuto, tutto il giorno, tutti i giorni |
| Ogni 5 minuti | */5 * * * * | minuto 0, 5, 10, …, 55 |
| Ogni 15 minuti | */15 * * * * | minuto 0, 15, 30, 45 |
| Ogni 30 minuti | */30 * * * * | minuto 0 e 30 |
| Ogni ora | 0 * * * * | allo scoccare di ogni ora |
| Ogni 2 ore | 0 */2 * * * | ora 0, 2, 4, …, 22 |
| Ogni 6 ore | 0 */6 * * * | ora 0, 6, 12, 18 |
| Due volte al giorno (9 e 21) | 0 9,21 * * * | minuto 0 delle ore 9 e 21 |
| Ogni giorno feriale alle 9 | 0 9 * * 1-5 | lun-ven 09:00 |
| Ogni fine settimana alle 9 | 0 9 * * 0,6 | sab e dom 09:00 |
| Ogni giorno a mezzanotte | 0 0 * * * | ogni giorno 00:00 |
| Ogni giorno alle 2:30 | 30 2 * * * | finestra batch a basso traffico |
| Ogni lunedì alle 9 | 0 9 * * 1 | lunedì 09:00 |
| Ogni venerdì alle 17 | 0 17 * * 5 | venerdì 17:00 |
| Ogni domenica a mezzanotte | 0 0 * * 0 | equivalente a @weekly |
| Primo del mese a mezzanotte | 0 0 1 * * | giorno 1 alle 00:00, equivalente a @monthly |
| Il 15 di ogni mese a mezzogiorno | 0 12 15 * * | finestra paghe di metà mese |
| Controllo ultimo giorno (wrapper) | 0 0 28-31 * * + script | richiede un controllo sulla data |
| Trimestrale (gen/apr/lug/ott il 1) | 0 0 1 JAN,APR,JUL,OCT * | primo giorno di ogni trimestre |
| Annuale (1° gennaio) | 0 0 1 1 * o @yearly | mezzanotte di Capodanno |
| Ogni 5 min, lun-ven 9-17 | */5 9-17 * * 1-5 | polling in orario d’ufficio |
| Ogni 30 min nel weekend | */30 * * * 0,6 | monitoraggio sab/dom |
| Due volte all’ora, ai minuti 15 e 45 | 15,45 * * * * | sfasamento dalla mandria allo :00 |
| Primo lunedì (wrapper) | 0 9 1-7 * 1 + controllo AND | serve un wrapper (vedi sotto) |
| Macro | @hourly @daily @weekly @monthly @yearly | non standard ma ampiamente supportate |
| Solo all’avvio | @reboot | non standard, solo vixie cron |
Incolla una qualsiasi di queste nel Generatore Crontab per vedere in anteprima le prossime cinque esecuzioni: è il controllo più rapido prima del deploy.
Sintassi cron decodificata: i 5 campi
Un’espressione cron è formata da cinque campi separati da spazi più un comando. Ogni campo controlla una porzione della pianificazione. Questo è il cuore della sintassi delle espressioni cron in tutti gli scheduler trattati in questa guida.
┌──────────── minuto (0 - 59)
│ ┌────────── ora (0 - 23)
│ │ ┌──────── giorno mese (1 - 31)
│ │ │ ┌────── mese (1 - 12 oppure JAN-DEC)
│ │ │ │ ┌──── giorno sett. (0 - 6 oppure SUN-SAT; 0 e 7 indicano entrambi la domenica)
│ │ │ │ │
* * * * * comando-da-eseguire
Mnemonica: “Mio Hat Doesn’t Match Wendy’s”, cioè Minute, Hour, Day-of-month, Month, Weekday. Da sinistra a destra, dall’unità più piccola alla più grande.
Valori ammessi campo per campo
| Campo | Intervallo | Alias | Note |
|---|---|---|---|
| Minuto | 0-59 | nessuno | 0 significa “allo scoccare dell’ora” |
| Ora | 0-23 | nessuno | formato 24h; 0 è mezzanotte, 12 è mezzogiorno |
| Giorno del mese | 1-31 | nessuno | i giorni non validi per un mese semplicemente non scattano (31 febbraio) |
| Mese | 1-12 | JAN, FEB, MAR, …, DEC | non distingue maiuscole/minuscole |
| Giorno della settimana | 0-7 | SUN, MON, TUE, …, SAT | sia 0 sia 7 indicano la domenica |
Operatori in dettaglio
Cinque operatori coprono qualsiasi espressione cron standard:
| Operatore | Significato | Esempio | Si espande in |
|---|---|---|---|
* | qualsiasi valore | * * * * * | ogni minuto |
, | elenco | 0 9,12,17 * * * | 09:00, 12:00, 17:00 |
- | intervallo inclusivo | 0 9-17 * * * | ogni ora dalle 09:00 alle 17:00 |
/ | passo | */15 * * * * | minuto 0, 15, 30, 45 |
| misto | combinato | 0 9-12,14-17 * * * | mattina + pomeriggio, salta la pausa pranzo |
L’operatore di passo merita attenzione. */N è ancorato al valore minimo del campo, non all’ora corrente. */15 significa “minuto 0, 15, 30, 45 di ogni ora”, non “ogni 15 minuti a partire da adesso”. Salva alle 12:03 e la prossima esecuzione è alle 12:15. Con una base diversa dal wildcard, 5/15 si legge “parti dal 5, poi ogni 15”: minuto 5, 20, 35, 50.
Mesi e giorni nominati
Scrivi mesi e giorni come nomi, senza distinguere maiuscole/minuscole:
0 0 1 JAN,APR,JUL,OCT * # primo giorno di ogni trimestre
0 9 * * MON-FRI # giorni feriali alle 9
0 17 * * FRI # venerdì alle 17
I nomi sono più leggibili in code review; le forme numeriche sono leggermente più portabili. Scegli uno stile per progetto.
Macro non standard: @reboot, @daily e affini
La maggior parte delle implementazioni cron accetta sei macro abbreviate:
| Macro | Si espande in | Significato |
|---|---|---|
@yearly / @annually | 0 0 1 1 * | una volta all’anno, 1° gennaio a mezzanotte |
@monthly | 0 0 1 * * | primo di ogni mese a mezzanotte |
@weekly | 0 0 * * 0 | ogni domenica a mezzanotte |
@daily / @midnight | 0 0 * * * | ogni giorno a mezzanotte |
@hourly | 0 * * * * | allo scoccare di ogni ora |
@reboot | (speciale) | una volta all’avvio del daemon cron |
Queste macro non sono standard: vixie cron e cronie le supportano, ma Kubernetes CronJob, GitHub Actions e AWS EventBridge no. Per espressioni portabili, scrivi la forma a cinque campi. @reboot di rado funziona nei container, dove il daemon cron potrebbe non essere il processo init.
50+ espressioni cron pronte da copiare (raggruppate per caso d’uso)
La tabella di riferimento rapido copre i casi comuni. Qui sotto trovi sei categorie con esempi di cron job più densi.
Ogni N minuti
* * * * * # ogni minuto
*/2 * * * * # ogni 2 minuti
*/5 * * * * # ogni 5 minuti, il classico cron expression every 5 minutes
*/10 * * * * # ogni 10 minuti
*/15 * * * * # ogni 15 minuti
*/30 * * * * # ogni 30 minuti
0,30 * * * * # minuti 0 e 30 espliciti (uguale a */30)
*/45 * * * * # ATTENZIONE: scatta solo a 0 e 45, poi riparte
*/45 è una trappola classica: il minuto va da 0 a 59, quindi finisce su 0 e 45 e poi riparte all’ora successiva. Per una cadenza vera di 45 minuti serve un worker esterno.
Varianti orarie
0 * * * * # ogni ora al :00
30 * * * * # ogni ora al :30
0 */2 * * * # ogni 2 ore, ora pari
0 */6 * * * # ogni 6 ore
0 */12 * * * # due volte al giorno alle 00:00 e 12:00
15 */2 * * * # ogni 2 ore, sfasato di 15 min (evita il picco delle :00)
Giornaliero a orari specifici
0 0 * * * # mezzanotte (= @daily / @midnight)
30 2 * * * # 02:30, finestra batch a basso traffico
0 9 * * * # 09:00
45 23 * * * # 23:45, riepiloghi di fine giornata
0 9,12,17 * * * # tre volte al giorno
0 9-17 * * * # ogni ora dalle 09:00 alle 17:00 incluse
Pianificazioni settimanali
0 9 * * 1-5 # giorni feriali alle 9
0 9 * * 0,6 # weekend alle 9
0 18 * * 5 # venerdì alle 18
0 0 * * 0 # domenica a mezzanotte (= @weekly)
0 9 * * MON,WED,FRI # lun/mer/ven alle 9
*/30 9-17 * * 1-5 # ogni 30 min, orario d'ufficio, lun-ven
Mensile e trimestrale
0 0 1 * * # 1° del mese a mezzanotte (= @monthly)
0 0 15 * * # il 15, finestra paghe
0 0 1,15 * * # 1° e 15, quindicinale
0 0 1 */3 * # trimestrale: primo di gennaio, aprile, luglio, ottobre
0 0 1 JAN,APR,JUL,OCT * # uguale, con mesi nominati
0 0 28-31 * * # ultimi giorni, abbina un wrapper che controlli la data
L’ultimo giorno del mese non ha un’espressione POSIX nativa. Esegui un wrapper che verifichi date -d tomorrow +%d = 01, oppure usa uno scheduler con supporto nativo (Quartz ha L; Kubernetes no).
Annuale e scorciatoie macro
0 0 1 1 * # 1° gennaio a mezzanotte (= @yearly / @annually)
0 0 25 12 * # Natale a mezzanotte
@yearly # = 0 0 1 1 *
@monthly # = 0 0 1 * *
@weekly # = 0 0 * * 0
@daily # = 0 0 * * *
@hourly # = 0 * * * *
@reboot # speciale: una volta all'avvio del daemon (solo vixie cron)
Tutte queste espressioni si incollano nel Generatore Crontab per vedere in anteprima le prossime cinque esecuzioni: è il test fumo più economico prima del deploy.
Cron vs systemd timer vs scheduler cloud: matrice decisionale
Cron è la scelta predefinita, non sempre la migliore. Qui trovi i sette scheduler più diffusi a confronto, utili per la scelta cron vs systemd timer, Kubernetes CronJob vs Vercel cron job, oppure per migrare da crontab al cloud gestito.
| Caratteristica | vixie cron | systemd timer | K8s CronJob | GHA schedule | AWS EventBridge | Vercel Cron | Cloudflare Workers |
|---|---|---|---|---|---|---|---|
| Sintassi dei campi | POSIX a 5 campi | spec OnCalendar | POSIX a 5 campi + timeZone | POSIX a 5 campi | Quartz a 6 campi con ? | POSIX a 5 campi | POSIX a 5 campi |
| Intervallo minimo | 1 minuto | 1 secondo | 1 minuto | best-effort, ≥15 min consigliati | 1 minuto | 1 minuto (piano Pro) | 1 minuto |
| Fuso orario esplicito | CRON_TZ= | Persistent=true | spec.timeZone (1.27+) | solo UTC | ScheduleExpressionTimezone | solo UTC | solo UTC |
| Recupero esecuzioni mancate | no (usa anacron) | sì (Persistent=true) | sì (startingDeadlineSeconds) | no | sì | no | no |
| Retry / backoff | no | parziale | sì (backoffLimit) | retry su errore | sì | no | sì |
| Controllo della concorrenza | no (usa flock) | parziale | sì (concurrencyPolicy) | no | no | no | no |
Supporto a @reboot | sì | sì (via OnBootSec=) | no | no | no | no | no |
systemd timer: quando preferirli a cron
Su Linux basato su systemd, i timer sono un’alternativa seria: sintassi calendariale leggibile, integrazione con il journal, recupero delle esecuzioni mancate. Un timer e il relativo service:
# daily-report.timer
[Unit]
Description=Run daily report at 9 AM
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
Unit=daily-report.service
[Install]
WantedBy=timers.target
# daily-report.service
[Unit]
Description=Daily report job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/daily-report.sh
User=reporter
Abilita con systemctl enable --now daily-report.timer. La feature decisiva è Persistent=true: se la macchina era spenta alle 9, il timer scatta non appena si accende, mentre vixie cron non ha equivalenti senza anacron. Per l’hardening dei servizi, leggi le nostre best practice di sicurezza.
Kubernetes CronJob
Kubernetes avvolge la pianificazione POSIX con primitive per concorrenza, cronologia e fuso orario esplicito:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
timeZone: "America/New_York" # Kubernetes 1.27+
concurrencyPolicy: Forbid # mai due esecuzioni in parallelo
startingDeadlineSeconds: 300 # salta se in ritardo di oltre 5 min
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: reporter
image: reporter:1.4.0
command: ["/usr/local/bin/report.sh"]
concurrencyPolicy: Forbid è l’equivalente di flock. Senza di esso, un job lungo si accumula sopra al successivo. Vedi la sezione di riferimento dei campi per tutte le opzioni.
Avvertenze su GitHub Actions schedule
GitHub Actions accetta cron POSIX standard a cinque campi:
on:
schedule:
- cron: '0 9 * * 1-5' # giorni feriali alle 9 UTC
Best-effort: con i runner di GitHub sotto carico, i job possono scattare con minuti di ritardo o saltare del tutto. Evita intervalli inferiori ai quindici minuti. Niente impostazione del fuso orario: sempre UTC.
AWS EventBridge: Quartz a sei campi
AWS EventBridge usa un cron in stile Quartz con sei campi e un ? obbligatorio in uno dei due slot del giorno:
cron(0 9 * * ? *)
Ordine dei campi: Minuti Ore Giorno-del-mese Mese Giorno-della-settimana Anno. Uno dei due campi del giorno deve essere ? quando l’altro è ristretto (la soluzione Quartz per risolvere l’ambiguità OR di POSIX). Una copia diretta da crontab Linux non passa la validazione.
Vercel Cron, Cloudflare Workers, Render Cron Jobs
Le piattaforme serverless più recenti si sono allineate sul POSIX a cinque campi. Un cron job di Vercel vive in vercel.json come { "crons": [{ "path": "/api/cron/nightly", "schedule": "0 2 * * *" }] }. I Cron Trigger di Cloudflare Workers usano wrangler.toml:
[triggers]
crons = ["*/15 * * * *", "0 9 * * 1-5"]
Render usa render.yaml. Tutti e tre girano in UTC senza override del fuso orario per singola pianificazione: progetta in UTC fin dall’inizio.
7 trappole di debugging per cron (e come scovarle)
La maggior parte dei “il mio cron job non parte” ha una di sette cause principali. Scorri questa lista prima di prendertela con lo scheduler.
Trappola 1: PATH minimale
Cron avvia i job con un $PATH minimale (spesso /usr/bin:/bin). La tua shell interattiva ha /usr/local/bin, ~/.cargo/bin e una dozzina di voci nel .bashrc. Nessuna esiste in cron. È il problema numero uno nel cron debugging path environment.
Sintomo: node: command not found. Fix: imposta PATH all’inizio del crontab oppure usa percorsi assoluti.
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
*/15 * * * * /usr/local/bin/poll-api.sh
0 9 * * * /home/deploy/.cargo/bin/my-rust-cli
Trappola 2: stdout e stderr vengono persi in silenzio
L’output di cron finisce, in modo predefinito, in una mail spool che nessuno legge. Il job fallisce in silenzio. Reindirizza entrambi gli stream:
*/15 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
Per l’output JSON, passa per jq; per estrarre righe di log, vedi il nostro cheat sheet sulle espressioni regolari. Per i systemd timer, journalctl -u tuo-timer.service cattura l’output.
Trappola 3: deriva di fuso orario tra dev e prod
Hai scritto 0 9 * * * sul portatile a New York pensando alle 9 Eastern. Il server gira in UTC. Il cron scatta alle 9 UTC, cioè le 4 Eastern, prima che qualcuno se ne accorga. Fix: imposta i server in UTC e scrivi le pianificazioni in UTC, oppure fissa esplicitamente il fuso orario.
CRON_TZ=America/New_York
0 9 * * * /usr/local/bin/morning-report.sh
CRON_TZ funziona in vixie cron 3.0+; Kubernetes 1.27+ ha spec.timeZone; AWS EventBridge ha ScheduleExpressionTimezone; GitHub Actions è sempre UTC. Per UTC, ora legale e matematica dell’epoch, vedi la nostra guida ai timestamp Unix.
Trappola 4: % non escaped nei comandi
Cron tratta i % non escaped come newline: il resto della riga finisce sullo stdin del comando. Così date +"%Y-%m-%d" si rompe. Fai l’escape di ogni % con \%, oppure sposta la logica in uno script:
0 0 * * * echo "Run at $(date +"\%Y-\%m-\%d")" >> /tmp/log
Trappola 5: esecuzioni sovrapposte
Un job */5 * * * * che occasionalmente impiega sette minuti farà partire la prossima istanza prima che la precedente finisca. Due copie si contendono la stessa riga, lo stesso lock file e la stessa quota API. Serializza con flock:
*/5 * * * * flock -n /tmp/job.lock /usr/local/bin/job.sh
-n esce subito se il lock è già preso. Per Kubernetes, imposta concurrencyPolicy: Forbid. I permessi del lock file contano: vedi le best practice di sicurezza.
Trappola 6: @reboot nei container
@reboot parte una volta all’avvio del daemon cron. In una VM corrisponde al boot. In un container il daemon cron spesso non è PID 1 e potrebbe non partire affatto. Non usare @reboot nei container: metti la logica “una volta al boot” nell’entrypoint o in un init container.
Trappola 7: semantica OR POSIX tra giorno del mese e giorno della settimana
La trappola cron più costosa. Regola POSIX: quando sia il giorno del mese sia il giorno della settimana sono ristretti (nessuno dei due è *), la pianificazione scatta quando uno qualsiasi dei due corrisponde.
0 0 1 * 5 sembra “mezzanotte del 1°, solo se è venerdì”, ma scatta il 1° E ogni venerdì: da sei a dieci esecuzioni extra al mese.
# SBAGLIATO: sembra "1° del mese, solo se venerdì"
0 0 1 * 5
# GIUSTO: scegli un solo vincolo
0 0 1 * * # ogni 1° del mese
0 0 * * 5 # ogni venerdì
# Per la semantica AND serve un wrapper
0 0 1-7 * 5 [ "$(date +\%u)" = "5" ] && /script # solo il primo venerdì
Incolla le espressioni sospette nel Generatore Crontab: l’anteprima delle prossime esecuzioni rende ovvia la trappola OR.
Scheduler moderni: quando NON usare cron
Cron è giusto per “esegui questo comando più o meno a quest’ora, con cadenza fissa”. È sbagliato per diversi problemi adiacenti:
- Workflow con dipendenze (esegui A, poi B se A è andato a buon fine) → Airflow, Prefect, Dagster.
- Retry, backoff esponenziale, dead-letter queue → Temporal, AWS Step Functions, Sidekiq.
- Intervalli sotto al minuto → un worker a lunga durata che dorme tra un’iterazione e l’altra.
- Precisione al secondo → daemon dedicato; gli scheduler gestiti non garantiscono tempi esatti.
- Lavoro event-driven → webhook, code di messaggi, stream di change-data-capture.
Cron non scompare: Airflow, Step Functions e Sidekiq accettano tutti espressioni cron per l’ingresso dei loro workflow. La grammatica a cinque campi è riusabile.
Riferimento dei campi Kubernetes CronJob
La matrice qui sopra mostra un CronJob minimo. Il riferimento completo dei campi per la sintassi kubernetes cronjob:
| Campo | Predefinito | Cosa fa |
|---|---|---|
schedule | obbligatorio | espressione cron POSIX a 5 campi |
timeZone | TZ del controller | fuso orario esplicito (1.27+); usa nomi IANA |
concurrencyPolicy | Allow | Forbid salta nuove esecuzioni mentre la precedente è attiva; Replace la annulla |
startingDeadlineSeconds | senza limite | salta se il ritardo supera questo valore |
successfulJobsHistoryLimit | 3 | quanti Job riusciti conservare |
failedJobsHistoryLimit | 1 | quanti Job falliti conservare |
suspend | false | metti in pausa senza eliminare |
backoffLimit | 6 | retry del Pod prima che il Job sia marcato come Failed |
activeDeadlineSeconds | non impostato | tetto rigido al runtime del Pod |
ttlSecondsAfterFinished | non impostato | auto-elimina il Job dopo questi secondi |
Due insidie ricorrenti: dimenticare timeZone fa seguire alla pianificazione il fuso orario dell’host del kube-controller-manager (imprevedibile su Kubernetes gestito); su una pianificazione al minuto, il valore predefinito successfulJobsHistoryLimit: 3 accumula tre oggetti Job al minuto se non imposti ttlSecondsAfterFinished.
Equivalenti cron multi-piattaforma
launchd su macOS. Apple raccomanda launchd al posto di cron. Un job launchd è un .plist in ~/Library/LaunchAgents/:
<plist version="1.0"><dict>
<key>Label</key><string>com.example.daily</string>
<key>ProgramArguments</key><array><string>/usr/local/bin/daily.sh</string></array>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
</dict></plist>
Caricalo con launchctl load ~/Library/LaunchAgents/com.example.daily.plist. A differenza di cron, launchd recupera le esecuzioni perse dopo sleep/wake.
Windows Task Scheduler usa schtasks:
schtasks /create /tn "DailyReport" /tr "C:\scripts\report.bat" /sc DAILY /st 09:00
schtasks /create /tn "EveryFifteen" /tr "C:\scripts\poll.bat" /sc MINUTE /mo 15
Su WSL il cron Linux nativo funziona, ma si ferma quando la sessione termina: usa Task Scheduler per avviare job WSL sempre attivi.
Cron nei container Docker. La maggior parte delle immagini snelle (alpine, debian-slim, distroless) non include un daemon cron. Installa cronie o busybox-cron e fallo girare come PID 1 con tini o s6-overlay. Quasi sempre, però, conviene un Kubernetes CronJob.
Consigli e pattern avanzati
Ultimo giorno del mese
Cron non ha un operatore “ultimo giorno” nativo. Esegui ogni giorno nella finestra 28-31 e controlla se il giorno dopo è il 1°:
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/eom.sh
N-esimo giorno della settimana del mese
“Primo lunedì” usa lo stesso pattern wrapper: restringi ai giorni 1-7 e poi controlla il giorno della settimana:
0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /usr/local/bin/first-monday.sh
Per “ultimo venerdì”, usa i giorni 25-31 più il controllo sul giorno della settimana.
Sfasamento casuale per distribuire il carico
Quando molte macchine eseguono lo stesso cron, 0 0 * * * produce una mandria che si scatena alla mezzanotte UTC. Aggiungi un ritardo casuale:
RANDOM_DELAY=10 # cronie / anacron, in minuti
0 0 * * * /usr/local/bin/job.sh
0 0 * * * sleep $((RANDOM \% 600)); /usr/local/bin/job.sh # portabile
Monitoraggio heartbeat
Cron fallisce in silenzio. Il pattern dead-man’s-switch funziona così: il job fa un ping a un servizio di monitoraggio dopo ogni esecuzione riuscita e il servizio avvisa quando il ping atteso non arriva. Healthchecks.io, Cronitor e Dead Man’s Snitch offrono piani gratuiti.
*/15 * * * * /usr/local/bin/job.sh && curl -fsS --retry 3 https://hc-ping.com/your-uuid
Per logiche di monitoraggio che si diramano in base al codice di risposta (200 healthy, 429 rate-limited, 503 degraded), vedi il nostro cheat sheet sui codici di stato HTTP.
L’idempotenza è una proprietà del job, non dello scheduler
Cron non ha retry, recupero di esecuzioni mancate, controllo della concorrenza. La soluzione più affidabile è rendere il job stesso sicuro da eseguire più volte. Invece di “invia il report di oggi alle 9”, progettalo come “invia il report di oggi se non è già stato inviato”: esecuzioni mancate, duplicati e riavvii manuali convergono tutti sullo stesso stato.
FAQ
*/5 * * * * è davvero ogni 5 minuti?
Quasi: */5 * * * * è ancorato al minuto 0, non “ogni 5 minuti da ora”. Scatta al minuto 0, 5, 10, …, 55 di ogni ora. Il passo */N è relativo al valore minimo del campo, non all’ora corrente. Salva alle 12:03 e la prossima esecuzione è alle 12:05, non alle 12:08.
Cosa significa 0 0 * * * in cron?
0 0 * * * significa ogni giorno a mezzanotte (00:00) nel fuso orario locale del server. Campi: minuto 0, ora 0, qualsiasi giorno del mese, qualsiasi mese, qualsiasi giorno della settimana. Equivalente alle macro @daily o @midnight. Per fissare il fuso orario, aggiungi CRON_TZ=America/New_York all’inizio del crontab.
Come faccio a eseguire un cron job ogni 30 secondi?
Non puoi con il cron POSIX standard: la granularità minima è un minuto. Tre soluzioni: due job sfasati su * * * * * con sleep 30 && su uno; un systemd timer con OnCalendar=*:*:0/30; oppure un worker a lunga durata che dorme tra un’iterazione e l’altra. L’ultima è quasi sempre la scelta giusta.
Quale fuso orario usa cron per impostazione predefinita?
Il fuso orario locale del server (/etc/timezone o la variabile d’ambiente TZ). Un cron alle 9 su un server UTC scatta alle 4 del fuso US East. Fix: imposta CRON_TZ= all’inizio del crontab oppure metti i server in UTC e progetta le pianificazioni in UTC. GitHub Actions usa sempre UTC; Kubernetes 1.27+ supporta spec.timeZone.
Perché il mio cron job non parte?
Se il tuo cron job non parte, controlla in quest’ordine: il daemon cron è in esecuzione (systemctl status cron); $PATH è impostato nel crontab; stderr è catturato (>> log 2>&1); il crontab dell’utente è caricato (crontab -l); i % sono escaped nei comandi; il fuso orario è quello che ti aspetti. La maggior parte dei “non parte” è la seconda o la terza voce.
La sintassi di Kubernetes CronJob è la stessa del cron Linux?
Sì per il campo schedule: entrambi usano cron POSIX a cinque campi. Kubernetes aggiunge spec.timeZone (1.27+), concurrencyPolicy per il controllo delle sovrapposizioni, startingDeadlineSeconds per il recupero delle esecuzioni mancate e suspend: true per mettere in pausa. Il cron Linux non ha nulla di tutto questo: ricorri a flock e anacron.
Qual è la differenza tra @reboot e @daily?
@daily è una macro per 0 0 * * *, cioè ogni giorno a mezzanotte su una pianificazione fissa. @reboot parte una volta all’avvio del daemon cron, senza pianificazione ricorrente. @reboot è supportato da vixie cron e cronie ma non da Kubernetes CronJob, GitHub Actions o AWS EventBridge. Nei container, @reboot scatta di rado.
Qual è la differenza tra cron e crontab?
cron è il demone in background che esegue i job pianificati; crontab è il file che li elenca (e il comando crontab per modificarlo). Il demone legge il crontab di ogni utente periodicamente ed esegue i comandi il cui orario di esecuzione corrisponde all’espressione cron. In breve: cron è il motore, crontab è la ricetta.