Cron — zaawansowana konfiguracja i typowe pułapki
Przypomnienie składni crontab
# ┌───────────── minuta (0-59)
# │ ┌───────────── godzina (0-23)
# │ │ ┌───────────── dzień miesiąca (1-31)
# │ │ │ ┌───────────── miesiąc (1-12)
# │ │ │ │ ┌───────────── dzień tygodnia (0-7, 0 i 7 = niedziela)
# │ │ │ │ │
# * * * * * polecenie
# Przykłady
0 2 * * * backup.sh # Codziennie o 2:00
*/5 * * * * check-health.sh # Co 5 minut
0 0 1 * * monthly-report.sh # 1. dnia każdego miesiąca o północy
0 9-18 * * 1-5 send-digest.sh # Co godzinę 9-18, pn-pt Powiązane tematy: podstawy cron job, backup strategia, logi serwera oraz monitorowanie uptime. Do wyboru hostingu z cron job sprawdź kalkulator kosztów.
Zaawansowane opcje składni
# Skróty @
@reboot polecenie # Uruchom raz przy starcie systemu
@hourly polecenie # Co godzinę (0 * * * *)
@daily polecenie # Raz dziennie (0 0 * * *)
@weekly polecenie # Raz tygodniowo (0 0 * * 0)
@monthly polecenie # Raz miesięcznie (0 0 1 * *)
@yearly polecenie # Raz rocznie (0 0 1 1 *)
# Kroki
*/10 * * * * # Co 10 minut
0 */4 * * * # Co 4 godziny
# Zakresy i listy
0 9,12,17 * * * # O 9:00, 12:00, 17:00
0 0 * * 1,3,5 # W poniedziałek, środę, piątek
# Ostatni dzień miesiąca (cronie)
0 0 L * * # Nie wszystkie implementacje wspierają L Zmienne środowiskowe w crontab
Środowisko cron jest ubogie — nie ma PATH, HOME ani
zmiennych z .bashrc. Zawsze definiuj PATH explicite:
# Edytuj crontab
crontab -e
# Na początku pliku — definicja środowiska
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
HOME=/root
MAILTO="" # Wyłącz emaile z błędami (domyślnie cron wysyła mail)
# Zamiast krótkiej nazwy używaj pełnej ścieżki
0 2 * * * /usr/bin/php /var/www/app/artisan schedule:run
# Lub załaduj .bashrc (workaround)
0 2 * * * source /root/.bashrc && backup.sh Logowanie wyjścia i błędów
# Przekieruj stdout i stderr do pliku logu
0 2 * * * /usr/bin/php backup.php >> /var/log/cron-backup.log 2>&1
# Tylko błędy (stdout do /dev/null)
0 2 * * * /usr/bin/php backup.php > /dev/null 2>> /var/log/cron-errors.log
# Wyjście z timestampem
0 2 * * * echo "$(date): START" >> /var/log/backup.log && backup.sh >> /var/log/backup.log 2>&1
# Rotacja logów cronowych (dodaj do /etc/logrotate.d/cron-custom)
/var/log/cron-backup.log {
weekly
rotate 4
compress
missingok
notifempty
} Lockfile — zapobieganie równoległym uruchomieniom
Problem: cron uruchamia nowe zadanie zanim poprzednie się skończyło. Rozwiązanie — lockfile:
#!/bin/bash
# backup.sh z lockfile
LOCKFILE="/tmp/backup.lock"
# Sprawdź czy inny proces nie działa
if [ -f "$LOCKFILE" ]; then
PID=$(cat "$LOCKFILE")
if kill -0 "$PID" 2>/dev/null; then
echo "$(date): Backup już uruchomiony (PID $PID), pomijam" >> /var/log/backup.log
exit 0
fi
fi
# Stwórz lockfile z PID
echo $$ > "$LOCKFILE"
# Posprzątaj lockfile przy wyjściu (nawet przy błędzie)
trap "rm -f $LOCKFILE" EXIT
echo "$(date): Backup start" >> /var/log/backup.log
# ... właściwa logika backupu ...
echo "$(date): Backup koniec" >> /var/log/backup.log Alternatywa: użyj flock:
# W crontab — flock zapewni że działa tylko jedna instancja
0 2 * * * flock -n /tmp/backup.lock /usr/local/bin/backup.sh WordPress — system cron vs wp-cron
Domyślny wp-cron WordPress jest wyzwalany przez wizytę na stronie — zawodny (nie uruchamia się gdy brak ruchu), spowalnia pierwsze żądanie każdej wizyty. Rekomendacja: wyłącz wp-cron i zastąp systemowym cronem.
# 1. Wyłącz wp-cron w wp-config.php
define('DISABLE_WP_CRON', true); # 2. Dodaj do crontab — wyzwalaj WP-Cron co 5 minut
*/5 * * * * /usr/bin/php /var/www/html/wp-cron.php > /dev/null 2>&1
# Lub przez WP-CLI (bardziej niezawodne)
*/5 * * * * /usr/local/bin/wp --path=/var/www/html cron event run --due-now > /dev/null 2>&1 Systemd timers — nowoczesna alternatywa dla cron
Na VPS z systemd można używać timerów systemd zamiast crona — oferują lepszą obsługę błędów, integrację z journald i możliwość startu po boocie z opóźnieniem.
# /etc/systemd/system/backup.service
[Unit]
Description=Backup aplikacji
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=www-data
StandardOutput=journal
StandardError=journal # /etc/systemd/system/backup.timer
[Unit]
Description=Codziennie backup o 2:00
[Timer]
OnCalendar=daily
RandomizedDelaySec=300 # Losowe opóźnienie do 5 minut (rozłożenie obciążenia)
Persistent=true # Uruchom jeśli pominięto (np. serwer był wyłączony)
[Install]
WantedBy=timers.target sudo systemctl enable --now backup.timer
# Status
sudo systemctl status backup.timer
sudo systemctl list-timers --all | Cecha | Cron | Systemd timer |
|---|---|---|
| Logowanie | Ręczne (redirect) | Automatyczne (journald) |
| Obsługa błędów | Email (często blokowany) | systemctl status, journalctl |
| Opóźnienie po starcie | Nie | Tak (OnBootSec) |
| Pominięte zadania | Gubione | Można odrobić (Persistent=true) |
| Prostota | Prosta składnia | Dwa pliki |
Debugowanie problemów z cron
# Sprawdź logi systemowe crona (Debian/Ubuntu)
grep CRON /var/log/syslog | tail -20
# Lub w systemd
journalctl -u cron --since "1 hour ago"
# Sprawdź czy cron jest uruchomiony
systemctl status cron
# Wyświetl aktualny crontab
crontab -l
# Crontab dla root
sudo crontab -l
# Pliki cron.d, cron.daily itp.
ls /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/