Espressioni cron spiegate: campi, passi e fusi orari
Cron è lo scheduler che esegue comandi a orari fissi sui sistemi di tipo Unix, e il suo formato a cinque campi si è diffuso ben oltre: GitHub Actions, Kubernetes e molti pannelli di hosting usano la stessa notazione. Una riga come 30 2 * * 1 si legge «alle 02:30 ogni lunedì». Quasi tutto il formato è semplice; alcune regole no, e sono quelle dietro ai job che partono all’ora sbagliata, due volte o mai. Questa guida segue lo standard POSIX e cronie, un cron molto diffuso su Linux, e segnala dove gli altri scheduler sono diversi.
I cinque campi
Una riga di crontab ha cinque campi di tempo separati da spazi, seguiti dal comando. Il job parte in ogni minuto che li soddisfa tutti, con un’eccezione per i due campi del giorno spiegata più avanti. Lo standard numera i giorni della settimana da 0 a 6, con 0 per la domenica; cronie e la maggior parte dei cron Linux accettano anche 7 per la domenica e nomi inglesi di tre lettere come mon o jan, maiuscoli o minuscoli.
| Campo | Valori | Esempio | Significa |
|---|---|---|---|
| Minuto | 0–59 | 30 | al minuto 30 |
| Ora | 0–23 | 2 | dalle 02:00 alle 02:59 |
| Giorno del mese | 1–31 | 1,15 | il 1° e il 15 |
| Mese | 1–12 o jan–dec | * | ogni mese |
| Giorno della settimana | 0–7 o sun–sat (0 e 7 sono domenica) | 1-5 | dal lunedì al venerdì |
Un asterisco indica tutti i valori del campo. Gli orari usano le 24 ore: 9 sono le 09:00 e 21 le 21:00.
Elenchi, intervalli e passi
Un campo può contenere un elenco (1,15), un intervallo (9-17) o un passo. Il passo suddivide un intervallo: 0-30/10 nel campo dei minuti indica i minuti 0, 10, 20 e 30, e */15 percorre tutti i valori dal primo, cioè i minuti 0, 15, 30 e 45. I passi si contano dall’inizio dell’intervallo e ripartono a ogni nuova ora, giorno o mese; non proseguono dal precedente.
*/15 * * * *Ogni 15 minuti: ai minuti 0, 15, 30 e 45.0 9-17 * * 1-5Allo scoccare di ogni ora dalle 09:00 alle 17:00, dal lunedì al venerdì.0 0 */7 * *A mezzanotte i giorni 1, 8, 15, 22 e 29: il conteggio riparte ogni mese.*/25 * * * *Ai minuti 0, 25 e 50, quindi tra :50 e il :00 successivo passano 10 minuti, non 25.
Nel campo dei minuti danno intervalli uguali solo i passi che dividono 60 esattamente (1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30). Ogni 90 minuti servono due righe: 0 0-21/3 * * * parte alle 00:00, 03:00 … 21:00, e 30 1-22/3 * * * alle 01:30, 04:30 … 22:30.
cronie accetta anche scorciatoie: @hourly, @daily (0 0 * * *), @weekly (0 0 * * 0), @monthly (0 0 1 * *), @yearly o @annually (0 0 1 1 *) e @reboot, che parte una volta all’avvio. Non fanno parte dello standard: GitHub Actions, per esempio, non le accetta.
Giorno del mese o giorno della settimana
Quando i campi del giorno del mese e del giorno della settimana contengono qualcosa di diverso da un asterisco, lo standard dice che un giorno va bene se corrisponde uno dei due, non entrambi. Per questo 0 9 13 * 5 parte alle 09:00 ogni 13 del mese e ogni venerdì, non solo il venerdì 13. A novembre 2026 sono il 6, il 13 (un venerdì, eseguito una volta), il 20 e il 27.
Per combinarli, pianifica un campo e lascia che il comando controlli l’altro. In una crontab un % nel comando diventa un a capo, quindi va scritto \%:
0 9 13 * * [ "$(date +\%u)" = 5 ] && /usr/local/bin/jobSolo il venerdì 13: parte ogni 13 e il job si avvia quando il giorno della settimana è 5 (venerdì).0 18 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /usr/local/bin/reportL’ultimo giorno del mese, con GNU date (Linux): parte dal 28 al 31 e il job si avvia quando domani è il 1°.
Fusi orari e ora legale
Cron legge la pianificazione nel fuso orario della macchina o del servizio che la esegue, che su server e piattaforme ospitate è spesso UTC. 0 9 * * * su un server UTC parte alle 10:00 o alle 11:00 a Roma, Parigi, Berlino o Madrid, a seconda della stagione, e alle 09:00 o alle 10:00 a Lisbona e Londra. Il Brasile non ha l’ora legale, quindi a San Paolo sono sempre le 06:00. cronie permette a una crontab di fissare il proprio fuso con la variabile CRON_TZ; GitHub Actions accetta una chiave timezone accanto a cron; Kubernetes ha il campo .spec.timeZone, stabile dalla versione 1.27, e rifiuta CRON_TZ o TZ scritti nella pianificazione.
L’ora legale fa sparire o ripetere alcune ore locali. Il 29 marzo 2026 gli orologi di Roma, Parigi, Berlino e Madrid passano dalle 02:00 alle 03:00, quindi 30 2 * * * indica un’ora che non esiste; il 25 ottobre 2026 l’ora dalle 02:00 alle 03:00 si ripete due volte. A Lisbona e Londra è l’ora dalle 01:00 alle 02:00.
| Scheduler | Job nell’ora saltata | Job nell’ora ripetuta |
|---|---|---|
| cronie (cambi sotto le 3 ore, job a ora fissa o meno frequenti di una volta l’ora) | parte subito dopo il cambio | parte una volta |
| GitHub Actions (con timezone) | passa all’ora valida successiva: 02:30 diventa 03:00 | non indicato |
| AWS EventBridge Scheduler (con fuso orario) | saltato | parte una volta |
| Pianificazioni in UTC | nessun effetto | nessun effetto |
cronie pianifica normalmente i job che partono più di una volta l’ora, e il suo manuale di crontab avverte in generale che le ore mancanti non corrispondono mai e quelle ripetute possono far partire il job due volte, quindi il comportamento cambia da un cron all’altro. La scelta sicura è tenere la pianificazione in UTC, oppure evitare le ore tra le 01:00 e le 03:00 locali per i job che devono partire esattamente una volta al giorno.
L’ambiente in cui gira un job
Un job di cron non riceve l’ambiente del tuo terminale. Lo standard esegue il comando con sh e un insieme minimo di variabili (HOME, LOGNAME, PATH e SHELL), quindi un comando che funziona quando lo digiti può fallire in cron perché un programma non è nel PATH o manca una variabile. Usa percorsi completi oppure imposta PATH all’inizio della crontab.
Tutto ciò che il comando stampa viene inviato per email al proprietario della crontab; cronie lo invia invece all’indirizzo in MAILTO, e non invia nulla se MAILTO è vuota. Su un server senza posta configurata quell’output si perde facilmente, quindi conviene mandarlo in un file di log.
0 3 * * * /usr/local/bin/backup >> /var/log/backup.log 2>&1Aggiunge sia l’output normale sia gli errori a un log.5 0 * * * tar czf /srv/backup-$(date +\%F).tgz /srv/dataCrea un archivio con la data nel nome: i segni % sono preceduti dalla barra.*/5 * * * * flock -n /tmp/sync.lock /usr/local/bin/syncSalta un’esecuzione finché la precedente tiene il blocco.
Cron avvia ogni esecuzione all’orario previsto, che la precedente sia finita o no, quindi un job che a volte dura più del suo intervallo può finire per girare due volte insieme. flock, di util-linux, tiene un file di blocco per tutta la durata del comando; con -n rinuncia subito, con codice di uscita 1, se il blocco è occupato. Kubernetes ha la stessa scelta integrata: concurrencyPolicy è Allow per impostazione predefinita e può essere Forbid (salta la nuova esecuzione) o Replace (ferma la vecchia).
La stessa pianificazione su altre piattaforme
Il formato a cinque campi non è universale. Alcuni scheduler aggiungono i secondi o un anno, altri richiedono un punto interrogativo in uno dei due campi del giorno perché non supportano la regola dell’uno o l’altro, e altri numerano i giorni della settimana da 1. «Nei giorni feriali alle 09:00» in ciascuno:
| Scheduler | Espressione | Note |
|---|---|---|
| Cron di Linux (cronie) | 0 9 * * 1-5 | Fuso della macchina, o CRON_TZ. |
| GitHub Actions | 0 9 * * 1-5 | UTC se non è indicato timezone; al massimo una volta ogni 5 minuti; niente scorciatoie con @. |
| Kubernetes CronJob | 0 9 * * 1-5 | Fuso del controller, o .spec.timeZone. |
| AWS EventBridge | cron(0 9 ? * MON-FRI *) | Sei campi con l’anno; ? in un campo del giorno; i giorni 1–7 iniziano dalla domenica; le regole pianificate usano UTC. |
| Quartz (Java) | 0 0 9 ? * MON-FRI | Prima i secondi, anno facoltativo; ? in un campo del giorno; i giorni 1–7 iniziano dalla domenica. |
| Timer di systemd | OnCalendar=Mon..Fri *-*-* 09:00:00 | Giorno della settimana e data devono corrispondere entrambi; *-*~01 è l’ultimo giorno del mese. |
Quando copi una pianificazione da un sistema all’altro, controlla il numero di campi e come sono numerati i giorni della settimana: 1 è lunedì in cron, ma domenica in Quartz ed EventBridge.
Perché un job non è partito
- La macchina era spenta o in sospensione a quell’ora: cron non recupera le esecuzioni perse. Un timer di systemd con Persistent=true lo fa, all’avvio successivo.
- La pianificazione è in un altro fuso orario, di solito UTC.
- Il comando dipende dal PATH, da una variabile o dalla cartella corrente del tuo terminale.
- Un % non preceduto dalla barra ha troncato il comando in quel punto.
- Erano impostati sia il giorno del mese sia il giorno della settimana, e la regola dell’uno o l’altro l’ha fatto partire in altri giorni, oppure il job aspettava un giorno che non è mai arrivato.
- Su una piattaforma ospitata un’esecuzione è stata ritardata o scartata: GitHub avverte che le esecuzioni pianificate possono subire ritardi nei momenti di carico, come l’inizio di ogni ora, e disattiva le pianificazioni dei repository pubblici dopo 60 giorni senza attività.
Prima di fidarti di una pianificazione, rileggila campo per campo e, su Linux, controlla le prossime esecuzioni: systemd-analyze calendar le mostra per un’espressione di timer.
Domande frequenti
La domenica è 0 o 7?
Entrambi, in cronie e nella maggior parte dei cron Linux. Lo standard POSIX definisce solo da 0 a 6, con 0 per la domenica, quindi 0 è la scelta portabile.
Cron può far partire un job ogni 30 secondi?
No: l’unità più piccola è il minuto. Gli scheduler con un campo dei secondi, come Quartz, possono farlo; su Linux anche un timer di systemd.
Come faccio partire un job ogni 90 minuti?
Con due righe: 0 0-21/3 * * * e 30 1-22/3 * * *. Insieme partono ogni ora e mezza, dalle 00:00 alle 22:30.
Cosa significa 0 0 * * 0?
Mezzanotte all’inizio di ogni domenica. In cronie equivale a @weekly.
Perché il mio job parte in giorni che non ho scelto?
Probabilmente perché sono impostati entrambi i campi del giorno. Il cron standard avvia il job quando corrisponde il giorno del mese o il giorno della settimana.
Riferimenti
- The Open Group Base Specifications (POSIX.1-2024) — crontab
- cronie — crontab(5): tables for driving cron
- cronie — cron(8): daylight saving time and other time changes
- util-linux — flock(1)
- GitHub Docs — Events that trigger workflows: schedule
- Kubernetes documentation — CronJob
- Amazon EventBridge — Cron expressions for scheduled rules
- Amazon EventBridge Scheduler — Schedule types (time zones and daylight saving)
- Quartz Scheduler — CronTrigger tutorial
- systemd — systemd.timer(5) and systemd.time(7): calendar events
Guide correlate
- Indirizzi IP spiegati: IPv4, IPv6 e gli intervalli riservati
- Sottoreti e CIDR spiegati: prefissi, maschere e host utilizzabili
- Record DNS spiegati: A, AAAA, CNAME, MX, TXT e NS
- Un sito è down o il problema è solo tuo? Come verificarlo, passo per passo
- Codici di stato HTTP ed errori di Cloudflare: cosa significano 403, 404, 429, 500, 503 e 521
- Timestamp Unix e fusi orari spiegati
- Contare le date: giorni tra due date, anni bisestili e settimane ISO
- Fusi orari e ora legale: riunioni tra città
- Formati immagine spiegati: JPEG, PNG, WebP, AVIF e HEIC
- Le dimensioni di un’immagine spiegate: pixel, DPI, peso e qualità
- Le foto del telefono spiegate: HEIC, metadati EXIF e la tua privacy
- Robustezza delle password spiegata: lunghezza, entropia e passphrase
- Funzioni di hash spiegate: SHA-256, checksum e hashing delle password
- JSON Web Token spiegati: claim, firme e validazione sicura
- Le chiavi API spiegate: lunghezza, formato, archiviazione e rotazione
- Gli UUID spiegati: v4, v7 e come scegliere la versione giusta
- I codici QR spiegati: capacità, correzione degli errori e dimensioni di stampa
- Percentuali e IVA spiegate: aggiungere, scorporare e cumulare
- Errori comuni nel JSON e come correggerli
- I file CSV spiegati: separatori, virgolette, codifica ed Excel
- Le codifiche del testo spiegate: UTF-8, Base64 e codifica URL
- Espressioni regolari: classi, quantificatori e gruppi
- MB o MiB, Mbps o MB/s: dimensioni dei file e velocità
- Parametri UTM spiegati: etichettare i link per le analisi
- Punto di pareggio, margine e ROAS: i numeri di una piccola attività
- Tariffe freelance spiegate: tariffa oraria, tariffa giornaliera e ore fatturabili
- Modificare i PDF nel browser: cosa conservano unione, divisione e conversione