Skip to content
Powrót do bloga
Poradniki

Crontab Cheat Sheet: 50+ wyrażeń cron i nowoczesne schedulery

Crontab cheat sheet: 50+ gotowych wyrażeń cron, składnia pól, pułapka OR dla dnia/tygodnia, strefy czasowe, porównanie Kubernetes/GitHub/AWS.

13 min czytania

Crontab cheat sheet: 50+ wyrażeń cron, składnia i przewodnik po nowoczesnych schedulerach

Wyrażenie cron to pięć pól (minuta, godzina, dzień miesiąca, miesiąc, dzień tygodnia), a po nich polecenie. Ta gramatyka rządzi harmonogramami w Unixie od 1979 roku i dziś napędza Kubernetes CronJob, GitHub Actions, AWS EventBridge oraz wyzwalacze cron w Vercel. Wystarczy nauczyć się raz, żeby planować zadania wszędzie.

Ta strona jest dla deweloperów, którzy potrzebują wyrażenia tu i teraz: zadanie w Linuksie, Kubernetes CronJob, wyzwalacz GitHub Actions albo diagnoza, dlaczego pięciominutowy job odpala co godzinę. Po gotowe wyrażenia można przejść do tabeli szybkiej referencji, po reguły pól do sekcji o składni, a sprawdzić wyrażenie na żywo pomoże Generator crontab — privacy-first alternatywa dla crontab guru działająca w przeglądarce.

Tabela szybkiej referencji wyrażeń cron

Trzydzieści wyrażeń pokrywa około 90% realnych potrzeb. Każde jest poprawnym pięciopolowym crone w wersji POSIX, można je wkleić do crontab -e, do schedule: w Kubernetes albo do cron: w GitHub Actions.

HarmonogramWyrażenie cronPo polsku
Co minutę* * * * *każda minuta przez całą dobę, każdego dnia
Co 5 minut*/5 * * * *minuta 0, 5, 10, …, 55
Co 15 minut*/15 * * * *minuta 0, 15, 30, 45
Co 30 minut*/30 * * * *minuta 0 i 30
Co godzinę0 * * * *o pełnej godzinie
Co 2 godziny0 */2 * * *godzina 0, 2, 4, …, 22
Co 6 godzin0 */6 * * *godzina 0, 6, 12, 18
Dwa razy dziennie (9:00 i 21:00)0 9,21 * * *minuta 0 godzin 9 i 21
Każdy dzień roboczy o 9:000 9 * * 1-5pn-pt 09:00
Każdy weekend o 9:000 9 * * 0,6sob i nd 09:00
Codziennie o północy0 0 * * *każdego dnia 00:00
Codziennie o 2:3030 2 * * *okno wsadowe o niskim ruchu
W każdy poniedziałek o 9:000 9 * * 1poniedziałki 09:00
W każdy piątek o 17:000 17 * * 5piątki 17:00
W każdą niedzielę o północy0 0 * * 0odpowiednik @weekly
Pierwszy dnia miesiąca o północy0 0 1 * *1. dzień 00:00, odpowiednik @monthly
15. dnia każdego miesiąca w południe0 12 15 * *środek miesiąca, okno wypłat
Kontrola ostatniego dnia (wrapper)0 0 28-31 * * + skryptwymaga sprawdzenia daty
Kwartalnie (1. dzień sty/kwi/lip/paź)0 0 1 JAN,APR,JUL,OCT *pierwszy dzień każdego kwartału
Rocznie (1 stycznia)0 0 1 1 * lub @yearlypółnoc Nowego Roku
Co 5 min w dni robocze 9-17*/5 9-17 * * 1-5odpytywanie w godzinach pracy
Co 30 min w weekendy*/30 * * * 0,6monitorowanie sob/nd
Dwa razy na godzinę, 15 i 4515,45 * * * *przesunięcie od szczytu o :00
Pierwszy poniedziałek (wrapper)0 9 1-7 * 1 + sprawdzenie ANDpotrzebny wrapper (zobacz niżej)
Makra@hourly @daily @weekly @monthly @yearlyniestandardowe, ale szeroko wspierane
Tylko przy restarcie@rebootniestandardowe, tylko vixie cron

Każde z tych wyrażeń można wkleić do Generator crontab, żeby zobaczyć podgląd kolejnych pięciu odpaleń. To najszybszy test przed wdrożeniem.

Składnia cron na czynniki pierwsze, 5 pól

Wyrażenie cron to pięć pól oddzielonych białymi znakami plus polecenie. Każde pole steruje jednym wymiarem harmonogramu. Tę samą składnię stosują wszystkie schedulery omawiane w tym przewodniku.

┌──────────── minuta       (0 - 59)
│ ┌────────── godzina      (0 - 23)
│ │ ┌──────── dzień miesiąca (1 - 31)
│ │ │ ┌────── miesiąc      (1 - 12 lub JAN-DEC)
│ │ │ │ ┌──── dzień tygodnia (0 - 6 lub SUN-SAT; 0 i 7 oznaczają niedzielę)
│ │ │ │ │
* * * * *  polecenie-do-uruchomienia

Mnemonik: „Moja Godzina Dnia Miesiąca Tygodnia” (Minuta, Godzina, Dzień miesiąca, Miesiąc, Dzień tygodnia). Od lewej do prawej, od najmniejszej jednostki do największej.

Dozwolone wartości pole po polu

PoleZakresAliasyUwagi
Minuta0-59brak0 oznacza „o pełnej godzinie”
Godzina0-23brakzegar 24-godzinny; 0 to północ, 12 to południe
Dzień miesiąca1-31braknieistniejące dni miesiąca po cichu nigdy nie odpalą (31 lutego)
Miesiąc1-12JAN, FEB, MAR, …, DECwielkość liter bez znaczenia
Dzień tygodnia0-7SUN, MON, TUE, …, SATzarówno 0, jak i 7 oznaczają niedzielę

Operatory szczegółowo

Pięć operatorów obsługuje każde standardowe wyrażenie cron:

OperatorZnaczeniePrzykładRozwija się do
*dowolna wartość* * * * *co minutę
,lista0 9,12,17 * * *09:00, 12:00, 17:00
-zakres włączny0 9-17 * * *co godzinę od 09:00 do 17:00
/krok*/15 * * * *minuta 0, 15, 30, 45
mieszanełączone0 9-12,14-17 * * *rano + popołudnie, z pominięciem lunchu

Operator kroku zasługuje na uwagę. */N jest zakotwiczony do najniższej wartości pola, a nie do bieżącej chwili. */15 znaczy „minuta 0, 15, 30, 45 każdej godziny”, a nie „co 15 minut od teraz”. Zapis o 12:03 daje kolejne odpalenie o 12:15. Z bazą inną niż wildcard 5/15 czyta się jako „startuj o 5, potem co 15”: minuta 5, 20, 35, 50.

Nazwane miesiące i dni tygodnia

Miesiące i dni tygodnia można zapisać nazwami, bez rozróżniania wielkości liter:

0 0 1 JAN,APR,JUL,OCT *    # pierwszy dzień każdego kwartału
0 9 * * MON-FRI            # dni robocze o 9:00
0 17 * * FRI               # piątek o 17:00

Nazwy czytają się czytelniej w code review; forma liczbowa jest nieco lepiej przenośna. W jednym projekcie warto trzymać się jednej konwencji.

Niestandardowe makra: @reboot, @daily i spółka

Większość implementacji cron akceptuje sześć makr skrótowych:

MakroRozwija się doZnaczenie
@yearly / @annually0 0 1 1 *raz w roku, 1 stycznia o północy
@monthly0 0 1 * *pierwszy dnia każdego miesiąca o północy
@weekly0 0 * * 0w każdą niedzielę o północy
@daily / @midnight0 0 * * *codziennie o północy
@hourly0 * * * *o pełnej godzinie
@reboot(specjalne)raz, przy starcie demona cron

Te makra są niestandardowe: vixie cron i cronie je obsługują, ale Kubernetes CronJob, GitHub Actions i AWS EventBridge już nie. Dla przenośnych wyrażeń lepiej pisać formę pięciopolową. @reboot rzadko działa w kontenerach, gdzie demon cron nie zawsze jest procesem init.

50+ gotowych wyrażeń cron (pogrupowane według zastosowań)

Tabela szybkiej referencji pokrywa typowe przypadki. Ta sekcja schodzi głębiej w sześć grup z gęstszymi przykładami cron jobów.

Co N minut

* * * * *          # co minutę
*/2 * * * *        # co 2 minuty
*/5 * * * *        # co 5 minut — klasyczny przypadek „cron expression every 5 minutes”
*/10 * * * *       # co 10 minut
*/15 * * * *       # co 15 minut
*/30 * * * *       # co 30 minut
0,30 * * * *       # jawnie minuty 0 i 30 (to samo, co */30)
*/45 * * * *       # UWAGA: odpala się tylko o 0 i 45, potem zawija

*/45 to typowy strzał w stopę: minuta ma zakres 0-59, więc wyrażenie ląduje na 0 i 45, a w następnej godzinie zawija się od zera. Dla prawdziwego cyklu co 45 minut potrzeba zewnętrznego workera.

Warianty godzinne

0 * * * *          # co godzinę o :00
30 * * * *         # co godzinę o :30
0 */2 * * *        # co 2 godziny, parzysta godzina
0 */6 * * *        # co 6 godzin
0 */12 * * *       # dwa razy dziennie, o 00:00 i 12:00
15 */2 * * *       # co 2 godziny, z przesunięciem 15 min (omija szczyt o :00)

Codziennie o konkretnej porze

0 0 * * *          # północ (= @daily / @midnight)
30 2 * * *         # 02:30 — okno wsadowe o niskim ruchu
0 9 * * *          # 09:00
45 23 * * *        # 23:45 — podsumowania na koniec dnia
0 9,12,17 * * *    # trzy razy dziennie
0 9-17 * * *       # co godzinę od 09:00 do 17:00 włącznie

Harmonogramy tygodniowe

0 9 * * 1-5        # dni robocze o 9:00
0 9 * * 0,6        # weekendy o 9:00
0 18 * * 5         # piątki o 18:00
0 0 * * 0          # niedziela o północy (= @weekly)
0 9 * * MON,WED,FRI # pon/śr/pt o 9:00
*/30 9-17 * * 1-5  # co 30 min, godziny pracy, dni robocze

Miesięczne i kwartalne

0 0 1 * *          # 1. dnia miesiąca o północy (= @monthly)
0 0 15 * *         # 15. — okno wypłat
0 0 1,15 * *       # 1. i 15. — co pół miesiąca
0 0 1 */3 *        # kwartalnie: pierwszy stycznia, kwietnia, lipca, października
0 0 1 JAN,APR,JUL,OCT *  # to samo, miesiące po nazwie
0 0 28-31 * *      # ostatnie kilka dni — sparować z wrapperem sprawdzającym datę

POSIX nie ma natywnego wyrażenia dla „ostatniego dnia miesiąca”. Trzeba uruchomić wrapper, który sprawdza date -d tomorrow +%d = 01, albo użyć schedulera z natywnym wsparciem (Quartz ma L, Kubernetes nie).

Rocznie i makra skrótowe

0 0 1 1 *          # 1 stycznia o północy (= @yearly / @annually)
0 0 25 12 *        # Boże Narodzenie o północy
@yearly            # = 0 0 1 1 *
@monthly           # = 0 0 1 * *
@weekly            # = 0 0 * * 0
@daily             # = 0 0 * * *
@hourly            # = 0 * * * *
@reboot            # specjalne: raz przy starcie demona (tylko vixie cron)

Wszystkie te wyrażenia można wkleić do Generator crontab, żeby zobaczyć podgląd kolejnych pięciu odpaleń. To najtańszy smoke test przed wdrożeniem.

Cron kontra timery systemd kontra schedulery chmurowe

Cron jest domyślnym wyborem, ale nie zawsze najlepszym. Poniżej porównanie siedmiu najczęściej spotykanych schedulerów. Przydaje się przy wyborze między cron a timerami systemd, między Kubernetes CronJob a wyzwalaczem cron w Vercel albo przy migracji z crontab do zarządzanej chmury.

Funkcjavixie crontimer systemdK8s CronJobGHA scheduleAWS EventBridgeVercel CronCloudflare Workers
Składnia pól5-polowa POSIXspec OnCalendar5-polowa POSIX + timeZone5-polowa POSIX6-polowy Quartz z ?5-polowa POSIX5-polowa POSIX
Minimalny interwał1 minuta1 sekunda1 minutabest-effort, zalecane ≥15 min1 minuta1 minuta (plan Pro)1 minuta
Jawna strefa czasowaCRON_TZ=Persistent=truespec.timeZone (1.27+)tylko UTCScheduleExpressionTimezonetylko UTCtylko UTC
Nadrabianie pominiętychnie (użyj anacron)tak (Persistent=true)tak (startingDeadlineSeconds)nietaknienie
Retry / backoffnieczęściowetak (backoffLimit)retry po porażcetaknietak
Kontrola współbieżnościnie (użyj flock)częściowatak (concurrencyPolicy)nienienienie
Wsparcie @reboottaktak (przez OnBootSec=)nienienienienie

Timery systemd, kiedy warto je wybrać zamiast cron

Na Linuksie opartym o systemd timery to poważna alternatywa: czytelna składnia kalendarzowa, integracja z journalem i nadrabianie pominiętych wywołań. Timer i odpowiadająca mu usługa wyglądają tak:

# 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

Włącza się to przez systemctl enable --now daily-report.timer. Mocnym argumentem jest Persistent=true: jeśli maszyna była wyłączona o 9:00, timer odpali się zaraz po starcie. Vixie cron nie ma odpowiednika bez anacron. O hardeningu usług piszemy w security best practices.

Kubernetes CronJob

Kubernetes opakowuje harmonogram POSIX prymitywami do kontroli współbieżności, historii i jawnej strefy czasowej:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-report
spec:
  schedule: "0 2 * * *"
  timeZone: "America/New_York"     # Kubernetes 1.27+
  concurrencyPolicy: Forbid        # nigdy nie odpalaj dwóch naraz
  startingDeadlineSeconds: 300     # pomiń, jeśli opóźnione >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 to odpowiednik flock. Bez tego długo działający job nakłada się na następcę. Wszystkie pokrętła opisuje sekcja referencyjna pól.

Pułapki schedulera GitHub Actions

GitHub Actions akceptuje standardowy pięciopolowy cron POSIX:

on:
  schedule:
    - cron: '0 9 * * 1-5'   # dni robocze o 9:00 UTC

Best-effort: przy wysokim obciążeniu runnerów GitHuba zadania potrafią odpalić się z kilkuminutowym opóźnieniem lub w ogóle wypaść. Interwałów krótszych niż piętnaście minut lepiej unikać. Strefy czasowej ustawić się nie da, zawsze UTC.

AWS EventBridge, sześciopolowy w stylu Quartz

AWS EventBridge używa cron o smaku Quartz z sześcioma polami i wymaganym ? w jednym ze slotów daty:

cron(0 9 * * ? *)

Kolejność pól: Minuty Godziny Dzień-miesiąca Miesiąc Dzień-tygodnia Rok. Jedno z pól dnia musi mieć wartość ?, jeśli drugie jest ograniczone (sposób, w jaki Quartz rozwiązuje POSIX-ową dwuznaczność OR). Bezpośrednie skopiowanie z linuksowego crontab nie przejdzie walidacji.

Vercel Cron, Cloudflare Workers, Render Cron Jobs

Nowsze platformy serverless standaryzują się wokół pięciopolowego POSIX. Vercel cron job zapisuje się w vercel.json jako { "crons": [{ "path": "/api/cron/nightly", "schedule": "0 2 * * *" }] }. Cron Triggers w Cloudflare Workers idą w wrangler.toml:

[triggers]
crons = ["*/15 * * * *", "0 9 * * 1-5"]

Render korzysta z render.yaml. Wszystkie trzy działają w UTC bez możliwości nadpisania strefy per harmonogram. Trzeba projektować od początku w UTC.

7 pułapek przy debugowaniu cron (i jak je łapać)

Większość zgłoszeń „mój cron job się nie uruchamia” ma jedną z siedmiu przyczyn źródłowych. Przed obwinianiem schedulera warto przejść tę listę.

Pułapka 1: minimalna PATH

Cron startuje zadania z minimalną $PATH, zwykle /usr/bin:/bin. Interaktywny shell ma /usr/local/bin, ~/.cargo/bin i tuzin wpisów z .bashrc. Pod cronem nie ma żadnego. To najczęstszy problem z PATH w środowisku cron.

Objaw: node: command not found. Naprawa: ustawić PATH na początku crontab albo używać ścieżek absolutnych.

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

Pułapka 2: stdout i stderr cicho znikają

Domyślnie wyjście crona idzie do mail spoola, którego nikt nie czyta. Zadanie kończy się porażką po cichu. Należy przekierować oba strumienie:

*/15 * * * *  /usr/local/bin/job.sh >> /var/log/job.log 2>&1

Dla wyjścia JSON podaj wynik przez jq; do wyłuskiwania linii logów zajrzyj do naszego regex cheat sheet. Dla timerów systemd wyjście zbiera journalctl -u your-timer.service.

Pułapka 3: dryf stref czasowych między dev i prod

Wyrażenie 0 9 * * * napisane na laptopie w Nowym Jorku miało odpalać o 9:00 czasu wschodniego. Serwer chodzi w UTC. Cron odpala się o 9:00 UTC, czyli o 4:00 czasu wschodniego, zanim ktokolwiek to zauważy. Naprawa: ustawić serwery na UTC i pisać harmonogramy w UTC albo jawnie przypiąć strefę.

CRON_TZ=America/New_York
0 9 * * *  /usr/local/bin/morning-report.sh

CRON_TZ działa w vixie cron 3.0+; Kubernetes 1.27+ ma spec.timeZone; AWS EventBridge ma ScheduleExpressionTimezone; GitHub Actions zawsze UTC. O UTC, DST i matematyce epoch piszemy w Unix timestamp guide.

Pułapka 4: nieuciekane % w poleceniach

Cron traktuje nieuciekane % jako znaki nowej linii i resztę wiersza wysyła do polecenia jako stdin. Przez to date +"%Y-%m-%d" się łamie. Każde % trzeba uciec jako \% albo przenieść logikę do skryptu:

0 0 * * *  echo "Run at $(date +"\%Y-\%m-\%d")" >> /tmp/log

Pułapka 5: nakładające się uruchomienia

Job */5 * * * *, który czasem zajmuje siedem minut, wystartuje kolejną instancję, zanim poprzednia skończy. Dwie kopie biją się o ten sam wiersz, plik blokady i limit API. Serializuje to flock:

*/5 * * * *  flock -n /tmp/job.lock /usr/local/bin/job.sh

-n powoduje natychmiastowe wyjście, jeśli blokada jest trzymana. W Kubernetesie odpowiednikiem jest concurrencyPolicy: Forbid. Uprawnienia plików blokady mają znaczenie, zobacz security best practices.

Pułapka 6: @reboot w kontenerach

@reboot uruchamia się raz, w momencie startu demona cron. W maszynie wirtualnej to moment bootowania. W kontenerze demon cron zwykle nie jest PID 1 i wcale może się nie uruchomić. @reboot w kontenerach lepiej odpuścić, a logikę „raz przy starcie” upchnąć w entrypoincie albo init container.

Pułapka 7: POSIX-owa semantyka OR dla dnia miesiąca i dnia tygodnia

Najbardziej kosztowna pułapka cron. Reguła POSIX: gdy oba pola, dzień miesiąca i dzień tygodnia, są ograniczone (żadne nie ma *), harmonogram odpala się, gdy pasuje dowolne z nich.

0 0 1 * 5 wygląda jak „o północy 1. dnia miesiąca, ale tylko w piątki”, a w rzeczywistości odpala się 1. dnia miesiąca ORAZ w każdy piątek: od sześciu do dziesięciu dodatkowych odpaleń miesięcznie.

# ŹLE: wygląda jak „1. dnia miesiąca, ale tylko jeśli piątek”
0 0 1 * 5
# DOBRZE: wybierz jedno ograniczenie
0 0 1 * *          # każdy 1. dzień miesiąca
0 0 * * 5          # każdy piątek
# Semantyka AND wymaga wrappera
0 0 1-7 * 5  [ "$(date +\%u)" = "5" ] && /script    # tylko pierwszy piątek

Podejrzane wyrażenia warto wkleić do Generator crontab. Podgląd kolejnego uruchomienia od razu pokazuje pułapkę OR.

Nowoczesne schedulery, kiedy NIE używać cron

Cron pasuje do „uruchom to polecenie mniej więcej o tej porze, w stałym cyklu”. Do kilku sąsiednich problemów już nie:

  • Workflow z zależnościami (uruchom A, potem B, jeśli A się udało): Airflow, Prefect, Dagster.
  • Retry, wykładniczy backoff, kolejki dead-letter: Temporal, AWS Step Functions, Sidekiq.
  • Interwały krótsze niż minuta: długo żyjący worker, który śpi między iteracjami.
  • Precyzja co do sekundy: dedykowany demon; zarządzane schedulery dystansują się od dokładnego timingu.
  • Praca sterowana zdarzeniami: webhooki, kolejki wiadomości, strumienie change-data-capture.

Cron nie znika. Airflow, Step Functions i Sidekiq nadal akceptują wyrażenia cron jako wejście swoich workflow. Pięciopolowa gramatyka jest wielokrotnego użytku.

Kubernetes CronJob, referencja pól {#kubernetes-cronjob-referencja-pol}

Macierz porównawcza powyżej pokazała minimalny CronJob. Pełna referencja pól dla składni Kubernetes CronJob:

PoleDomyślnieDo czego służy
schedulewymaganepięciopolowe wyrażenie cron POSIX
timeZoneTZ kontrolerajawna strefa czasowa (1.27+); użyj nazw IANA
concurrencyPolicyAllowForbid pomija nowe odpalenia, gdy poprzednie wciąż trwa; Replace anuluje poprzednie
startingDeadlineSecondsbez ograniczeńpomiń, jeśli opóźnienie przekracza tę wartość
successfulJobsHistoryLimit3liczba zachowanych udanych Jobów
failedJobsHistoryLimit1liczba zachowanych nieudanych Jobów
suspendfalsepauza bez usuwania
backoffLimit6liczba ponowień Poda, zanim Job zostanie oznaczony jako Failed
activeDeadlineSecondsnie ustawionetwardy limit czasu działania Poda
ttlSecondsAfterFinishednie ustawioneauto-usunięcie Jobu po podanej liczbie sekund

Dwie częste pułapki: pominięcie timeZone sprawia, że harmonogram idzie za strefą czasową hosta kube-controller-managera (na zarządzanym Kubernetesie nieprzewidywalna); przy harmonogramie minutowym domyślny successfulJobsHistoryLimit: 3 gromadzi trzy obiekty Job na minutę, dopóki nie ustawisz ttlSecondsAfterFinished.

Odpowiedniki cron na różnych platformach

macOS launchd. Apple zaleca launchd zamiast crona. Zadanie launchd to plik .plist w ~/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>

Ładuje się go przez launchctl load ~/Library/LaunchAgents/com.example.daily.plist. W odróżnieniu od cron, launchd nadrabia odpalenia pominięte podczas sleep/wake.

Windows Task Scheduler korzysta z 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

W WSL natywny linuksowy cron działa, ale zatrzymuje się wraz z końcem sesji. Żeby trzymać zadania WSL w trybie always-on, użyj Task Schedulera.

Cron w kontenerach Dockera. Większość slim images (alpine, debian-slim, distroless) nie ma demona cron. Trzeba zainstalować cronie albo busybox-cron i uruchomić go jako PID 1 razem z tini lub s6-overlay. Prawie zawsze lepiej jednak sięgnąć po Kubernetes CronJob.

Wskazówki i wzorce zaawansowane

Ostatni dzień miesiąca

Cron nie ma natywnego operatora „ostatniego dnia”. Trzeba uruchamiać się codziennie w oknie 28-31 i sprawdzić, czy jutro jest 1.:

0 23 28-31 * *  [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/eom.sh

N-ty dzień tygodnia w miesiącu

„Pierwszy poniedziałek” używa tego samego wzorca z wrapperem: ograniczyć do dni 1-7, potem sprawdzić dzień tygodnia:

0 9 1-7 * *  [ "$(date +\%u)" = "1" ] && /usr/local/bin/first-monday.sh

Dla „ostatniego piątku” weź dni 25-31 plus sprawdzenie dnia tygodnia.

Losowe przesunięcie, żeby rozproszyć obciążenie

Gdy wiele maszyn uruchamia ten sam cron, 0 0 * * * rodzi efekt stampede o północy UTC. Dodaj losowe opóźnienie:

RANDOM_DELAY=10                                              # cronie / anacron, w minutach
0 0 * * *  /usr/local/bin/job.sh

0 0 * * *  sleep $((RANDOM \% 600)); /usr/local/bin/job.sh   # przenośnie

Monitorowanie heartbeatu

Cron pada po cichu. Wzorzec dead-man’s switch: zadanie po każdym udanym uruchomieniu pinguje usługę monitorującą, a usługa alarmuje, gdy oczekiwany ping nie dotrze. Healthchecks.io, Cronitor i Dead Man’s Snitch oferują darmowe plany.

*/15 * * * *  /usr/local/bin/job.sh && curl -fsS --retry 3 https://hc-ping.com/your-uuid

Logikę monitorowania rozgałęzioną po kodach odpowiedzi (200 zdrowy, 429 rate-limited, 503 degradacja) opisuje nasz HTTP status codes cheat sheet.

Idempotencja to cecha zadania, nie schedulera

Cron nie ma retry, nie nadrabia pominiętych odpaleń i nie kontroluje współbieżności. Najpewniejszy fix to zrobienie samego zadania bezpiecznym do wielokrotnego uruchomienia. Zamiast „wyślij dzisiejszy raport o 9:00” zaprojektuj je jako „wyślij dzisiejszy raport, jeśli jeszcze nie został wysłany”. Pominięte odpalenia, duplikaty i ręczne nadrabianie zbiegają się wtedy do tego samego stanu.

FAQ

Czy */5 * * * * naprawdę odpala się co 5 minut?

Prawie — */5 * * * * jest zakotwiczone do minuty 0, a nie do „co 5 minut od teraz”. Odpala się o minucie 0, 5, 10, …, 55 każdej godziny. Krok */N jest względny wobec najniższej wartości pola, nie wobec bieżącej chwili. Zapis o 12:03 daje kolejne odpalenie o 12:05, nie o 12:08.

Co oznacza 0 0 * * * w cron?

0 0 * * * oznacza codziennie o północy (00:00) w lokalnej strefie czasowej serwera. Pola: minuta 0, godzina 0, dowolny dzień miesiąca, dowolny miesiąc, dowolny dzień tygodnia. Odpowiednik makr @daily lub @midnight. Żeby przypiąć strefę, dodaj CRON_TZ=America/New_York na początku crontab.

Jak uruchomić cron job co 30 sekund?

Standardowym POSIX-owym cronem się nie da, minimalna granularność to jedna minuta. Trzy obejścia: dwa zadania przesunięte o * * * * * z sleep 30 && na jednym; timer systemd z OnCalendar=*:*:0/30; albo długo żyjący worker śpiący między iteracjami. To ostatnie jest zwykle właściwe.

Jakiej strefy czasowej używa cron domyślnie?

Lokalnej strefy systemowej serwera (/etc/timezone lub zmienna środowiskowa TZ). Cron o 9:00 na serwerze w UTC odpali się o 4:00 czasu wschodniego. Naprawa: ustaw CRON_TZ= na początku crontab albo trzymaj serwery w UTC i projektuj harmonogramy w UTC. GitHub Actions zawsze chodzi w UTC; Kubernetes 1.27+ wspiera spec.timeZone.

Dlaczego mój cron job się nie uruchamia?

Jeśli twoje zadanie cron się nie uruchamia, sprawdź po kolei: czy demon cron działa (systemctl status cron); czy $PATH jest ustawiona w crontab; czy stderr jest przechwytywany (>> log 2>&1); czy crontab użytkownika jest załadowany (crontab -l); czy % w poleceniach jest uciekane; czy strefa czasowa jest taka, jak się spodziewasz. Większość zgłoszeń „nie działa” rozwiązuje punkt drugi lub trzeci.

Czy składnia Kubernetes CronJob jest taka sama jak linuksowego cron?

Tak, jeśli chodzi o pole schedule: oba używają pięciopolowego cron POSIX. Kubernetes dokłada spec.timeZone (1.27+), concurrencyPolicy do kontroli nakładania się, startingDeadlineSeconds do nadrabiania pominiętych odpaleń i suspend: true do pauzy. Linuksowy cron nie ma żadnego z tych mechanizmów, w jego miejscu sięga się po flock i anacron.

Czym różnią się @reboot i @daily?

@daily to makro dla 0 0 * * *, czyli codziennie o północy, w stałym harmonogramie. @reboot odpala się raz, przy starcie demona cron, bez cyklu. @reboot obsługują vixie cron i cronie, ale nie Kubernetes CronJob, GitHub Actions ani AWS EventBridge. W kontenerach @reboot rzadko się odpala.

Jaka jest różnica między cron a crontab?

cron to demon działający w tle, który uruchamia zaplanowane zadania; crontab to plik, który je wymienia (oraz polecenie crontab do edycji tego pliku). Demon okresowo czyta crontab każdego użytkownika i uruchamia polecenia, których czas wykonania pasuje do wyrażenia cron. Krótko mówiąc: cron to silnik, crontab to przepis.

Powiązane artykuły

Zobacz wszystkie artykuły