systemd timer vs cron: które narzędzie wybrać?
Opublikowano: 9 kwietnia 2026 · Kategoria: DevOps
Cron jest z nami od 1975 roku i przez dekady był jedynym rozsądnym sposobem na uruchamianie zadań cyklicznych w Linuksie. Od czasów systemd (2010) mamy jednak nową alternatywę — systemd timer — która rozwiązuje wiele bolączek crona: brak logowania, brak catchup po downtime, prymitywny harmonogram, brak zależności. W tym artykule porównamy oba narzędzia na konkretnych przykładach i pokażemy, kiedy warto przenieść crony do systemd timerów.
Jak działa cron
Cron to demon, który co minutę sprawdza plik /etc/crontab oraz crontaby użytkowników
i uruchamia zadania, których czas nadszedł. Składnia to pięć pól (minuta, godzina, dzień miesiąca,
miesiąc, dzień tygodnia) i polecenie do wykonania. Prosta, znana od pokoleń.
# Edytuj crontab uzytkownika
crontab -e
# Przyklad crontab: backup codziennie o 3:00
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Co 15 minut: sprawdzenie zdrowia
*/15 * * * * /usr/local/bin/healthcheck.sh
# W kazdy poniedzialek o 6:30
30 6 * * 1 /usr/local/bin/weekly-report.sh
# Co godzine w dniach roboczych (pon-pt)
0 * * * 1-5 /usr/local/bin/sync.sh
# Listuj aktywne crony
crontab -l
# Loguj wszystkie crony (wlacz w /etc/rsyslog.d/50-default.conf)
# odkomentuj: cron.* /var/log/cron.log
Problem z cronem jest taki, że nie loguje domyślnie niczego — jeśli twój skrypt wypluje błąd
na stderr, cron wyśle mail (o ile MTA jest skonfigurowany), a jeśli nie — błąd znika w
próżni. Dlatego konwencjonalnie przekierowuje się stdout i stderr do
pliku logu ręcznie. Kolejny problem: gdy serwer jest offline w momencie zaplanowanego uruchomienia,
cron pomija to zadanie i czeka na kolejne wystąpienie.
Anatomia systemd timer
systemd timer składa się z dwóch plików: .service (co uruchomić) i .timer
(kiedy uruchomić). Oba żyją w /etc/systemd/system/ lub ~/.config/systemd/user/
dla timerów użytkownika.
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup job
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal
StandardError=journal
Nice=10
IOSchedulingClass=idle
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 3am
Requires=backup.service
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=300
Unit=backup.service
[Install]
WantedBy=timers.target OnCalendar to najmocniejsza broń systemd timerów — obsługuje składnię dużo bogatszą
niż cron. Możesz użyć wyrażeń typu Mon..Fri 09:00, *-*-01 12:00 (każdy
pierwszy dzień miesiąca) albo *:0/15 (co 15 minut). Dodatkowo są predykaty: daily, weekly, monthly, hourly, yearly.
Zarządzanie timerami
Po utworzeniu plików musisz przeładować systemd i włączyć timer. systemd traktuje timery jak każdą inną jednostkę — enable, start, stop, status.
# Przeladuj konfiguracje systemd
sudo systemctl daemon-reload
# Wlacz timer (start po boot)
sudo systemctl enable backup.timer
# Uruchom timer teraz
sudo systemctl start backup.timer
# Status timera (kiedy uruchomi sie nastepnym razem)
systemctl status backup.timer
# Lista wszystkich timerow z nastepnym uruchomieniem
systemctl list-timers
# Lista razem z nieaktywnymi
systemctl list-timers --all
# Logi z backup.service (ostatnie uruchomienia)
journalctl -u backup.service --since today
# Logi z ostatniej godziny
journalctl -u backup.service --since "1 hour ago"
# Follow logow (tail -f)
journalctl -u backup.service -f
# Reczny start service (test bez czekania na timer)
sudo systemctl start backup.service Porównanie: cron vs systemd timer
| Cecha | cron | systemd timer |
|---|---|---|
| Prostota składni | Bardzo prosta | Złożona (2 pliki) |
| Logowanie | Ręczne (redirect) | journalctl (automatyczne) |
| Catchup (Persistent) | Brak (anacron osobno) | Wbudowane (Persistent=true) |
| Randomized delay | Brak | RandomizedDelaySec |
| Zależności | Brak | After, Requires, Wants |
| Monitoring stanu | Brak | systemctl status |
| Resource limits | Brak | Nice, IOClass, MemoryMax |
| OnBootSec (po starcie) | @reboot (prymitywne) | OnBootSec (dokładne) |
Zaawansowane wzorce OnCalendar i OnBootSec
Timer ma dwa niezależne typy wyzwalaczy: calendar-based (OnCalendar) i monotonic (OnBootSec, OnStartupSec, OnUnitActiveSec, OnUnitInactiveSec). Calendar jest czasem zegarowym (np. codziennie o 3:00), monotonic jest liczony od konkretnego zdarzenia (np. 5 minut po starcie systemu).
[Timer]
# Codziennie o polnocy
OnCalendar=daily
# Pierwszy dzien miesiaca
OnCalendar=monthly
# Pon-Pt o 9:00
OnCalendar=Mon..Fri 09:00
# Co 15 minut
OnCalendar=*:0/15
# 15 minut po starcie systemu
OnBootSec=15min
# 5 minut po starcie + co 30 minut pozniej
OnBootSec=5min
OnUnitActiveSec=30min
# Kilka roznych czasow dziennie
OnCalendar=*-*-* 06,12,18,22:00:00
# Test wyrazenia OnCalendar
# systemd-analyze calendar "Mon..Fri 09:00"
# -> Next elapse: Mon 2026-04-13 09:00:00 UTC
Komenda systemd-analyze calendar "wyrażenie" to nieoceniony helper — pozwala sprawdzić,
czy twoje wyrażenie OnCalendar jest poprawne i kiedy dokładnie zostanie uruchomione następnym
razem. Używaj jej zanim zapiszesz plik timer — oszczędzi wiele nerwów.