Yıllardır cron işlerini yönetirken bir gün “bu iş böyle olmaz” dediğim anı net hatırlıyorum. Bir script çalışmamış, log yok, hata yok, sessiz sedasız kaybolup gitmiş. Cron’un en büyük sorunu da bu zaten: bir şeyler ters gittiğinde karanlıkta kalıyorsunuz. Systemd timer’larını keşfettiğimde ise oyunun kurallarının değiştiğini anladım.
Systemd timer’lar, cron’un yerini almak için değil ama onu ciddi anlamda geride bırakmak için tasarlanmış gibi duruyor. Journald entegrasyonu, bağımlılık yönetimi, hassas zamanlama ve sistem başlangıcına göre göreceli zamanlamalar ile systemd timer’lar modern Linux yönetiminin vazgeçilmez bir parçası haline geldi.
Systemd Timer Nedir, Cron’dan Farkı Ne?
Cron, Unix dünyasının köklü bir geleneğidir. Basit, doğrudan ve hemen hemen her sistemde bulunur. Ama bazı ciddi eksiklikleri var:
- Log yönetimi zayıf: Cron çıktısını mail ile gönderir ya da bir yere yönlendirmeniz gerekir
- Bağımlılık desteği yok: Bir servis ayakta değilse cron işini yine de çalıştırmaya çalışır
- Sistem başlangıcına göre zamanlama yok: “Sistem açıldıktan 5 dakika sonra çalıştır” gibi bir şey yapılamaz
- Paralel çalışma kontrolü zor: Aynı iş iki kez çakışırsa ne olur? Cron umursamaz
- Merkezi yönetim yok:
systemctlile kontrol edilemez
Systemd timer’lar ise tam tersine tüm bu sorunları çözer. Her timer, bir .service dosyasına bağlıdır ve bu servis dosyası da systemd ekosistemiyle tam entegre çalışır.
Temel Yapı: Timer ve Service İkili
Systemd timer’ın en güzel yanı sadeliği. Her zamanlı görev için iki dosyaya ihtiyacınız var:
.timerdosyası: Ne zaman çalışacağını tanımlar.servicedosyası: Ne çalışacağını tanımlar
Bu dosyalar genellikle /etc/systemd/system/ dizinine yerleştirilir. Kullanıcı seviyesinde timer’lar için ~/.config/systemd/user/ dizini kullanılır.
İlk Timer’ınızı Yazalım
Basit bir örnek ile başlayalım. Diyelim ki her gece saat 02:00’de bir yedekleme scripti çalıştırmak istiyorsunuz.
Önce servis dosyasını oluşturun:
sudo nano /etc/systemd/system/yedekleme.service
[Unit]
Description=Gece Yedekleme Scripti
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/yedek-al.sh
User=root
StandardOutput=journal
StandardError=journal
Şimdi timer dosyasını oluşturun:
sudo nano /etc/systemd/system/yedekleme.timer
[Unit]
Description=Gece Yedekleme Timer
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
Ardından timer’ı etkinleştirip başlatın:
sudo systemctl daemon-reload
sudo systemctl enable --now yedekleme.timer
İşte bu kadar. Artık her gece saat 02:00’de yedekleme scriptiniz çalışacak.
OnCalendar Sözdizimi
OnCalendar direktifi, cron’un saat-dakika-gün formatının çok daha okunabilir bir versiyonu. Temel format şöyle:
DayOfWeek Year-Month-Day Hour:Minute:Second
Birkaç pratik örnek:
# Her gün saat 03:30'da
OnCalendar=*-*-* 03:30:00
# Her Pazartesi saat 08:00'de
OnCalendar=Mon *-*-* 08:00:00
# Her ayın 1'inde gece yarısı
OnCalendar=*-*-01 00:00:00
# Her 15 dakikada bir
OnCalendar=*:0/15
# Her Cuma saat 17:00'de
OnCalendar=Fri *-*-* 17:00:00
# Her iş günü (Pazartesi-Cuma) sabah 09:00'da
OnCalendar=Mon..Fri *-*-* 09:00:00
# Her saat başı
OnCalendar=hourly
# Her gün
OnCalendar=daily
# Her hafta
OnCalendar=weekly
Bir zaman ifadesinin ne zaman tetikleneceğini test etmek için şu komutu kullanabilirsiniz:
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
Bu komut size bir sonraki tetiklenme zamanını ve geçen son zamanı gösterir. Çok işe yarayan bir araç.
Göreceli Zamanlama: OnBootSec ve OnUnitActiveSec
Cron’da yapamadığınız ama çok ihtiyaç duyduğunuz şeylerden biri: “sistem açıldıktan X dakika sonra çalıştır.” Systemd timer’lar bunu iki direktifle çözer.
OnBootSec: Sistem açıldıktan belirli süre sonra çalıştır OnUnitActiveSec: Son çalışmadan belirli süre sonra tekrar çalıştır
[Timer]
# Sistem açıldıktan 5 dakika sonra ilk çalışma
OnBootSec=5min
# Sonrasında her 1 saatte bir çalış
OnUnitActiveSec=1h
Bu kombinasyon özellikle şu senaryolar için mükemmel:
- Sistem başlangıcında veritabanı bağlantısı hazır olmadan önce çalışmaması gereken görevler
- Her X dakikada bir health check yapan scriptler
- Servis başladıktan sonra warm-up süresi gerektiren uygulamalar
Zaman birimlerini de bilmekte fayda var:
- us veya usec: Mikrosaniye
- ms veya msec: Milisaniye
- s, sec, second, seconds: Saniye
- min, minute, minutes: Dakika
- h, hr, hour, hours: Saat
- d, day, days: Gün
- w, week, weeks: Hafta
- month, months: Ay
- y, year, years: Yıl
Gerçek Dünya Senaryosu 1: Log Temizleme
Birçok sistemde loglar zamanla şişer ve disk dolmaya başlar. Şöyle bir senaryo düşünelim: /var/log/uygulama/ dizininde 30 günden eski logları temek silmek istiyorsunuz.
# /usr/local/bin/log-temizle.sh
#!/bin/bash
LOG_DIR="/var/log/uygulama"
GUNE_GORE=30
find "$LOG_DIR" -name "*.log" -mtime +$GUNE_GORE -delete
echo "$(date): $LOG_DIR altındaki $GUNE_GORE günden eski loglar silindi"
# /etc/systemd/system/log-temizle.service
[Unit]
Description=Uygulama Log Temizleme
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/log-temizle.sh
User=root
# /etc/systemd/system/log-temizle.timer
[Unit]
Description=Haftalik Log Temizleme Timer
[Timer]
OnCalendar=Sun *-*-* 01:00:00
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true direktifi önemli. Eğer sistem kapalıyken timer zamanı geçmişse, sistem açıldığında hemen çalıştırır. Bu özellik özellikle laptop gibi her zaman açık olmayan sistemlerde hayat kurtarır.
Gerçek Dünya Senaryosu 2: Veritabanı Yedekleme
Bir PostgreSQL veritabanı yedekleme örneği yapalım. Bu sefer birkaç gelişmiş özellik de ekleyelim:
# /usr/local/bin/pg-yedek.sh
#!/bin/bash
set -euo pipefail
DB_ADI="uretim_db"
YEDEK_DIZIN="/mnt/yedekler/postgresql"
TARIH=$(date +%Y%m%d_%H%M%S)
YEDEK_DOSYA="${YEDEK_DIZIN}/${DB_ADI}_${TARIH}.dump"
mkdir -p "$YEDEK_DIZIN"
echo "Yedekleme başlıyor: $DB_ADI"
pg_dump -Fc -U postgres "$DB_ADI" > "$YEDEK_DOSYA"
# 7 günden eski yedekleri sil
find "$YEDEK_DIZIN" -name "*.dump" -mtime +7 -delete
echo "Yedekleme tamamlandı: $YEDEK_DOSYA"
# /etc/systemd/system/pg-yedek.service
[Unit]
Description=PostgreSQL Yedekleme
Requires=postgresql.service
After=postgresql.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/pg-yedek.sh
User=postgres
Group=postgres
StandardOutput=journal
StandardError=journal
# Yedekleme 1 saatten uzun sürerse öldür
TimeoutStartSec=3600
# /etc/systemd/system/pg-yedek.timer
[Unit]
Description=Gunluk PostgreSQL Yedekleme
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Burada dikkat çeken bir direktif var: RandomizedDelaySec. Bu, timer’ın belirtilen süreden önce rastgele bir gecikme eklemesini sağlar. Birden fazla sunucunuz varsa ve hepsi aynı anda yedekleme yapmaya başlarsa paylaşımlı depolama üzerinde ciddi yük oluşur. RandomizedDelaySec=300 ile her sunucu en fazla 5 dakika rastgele bekler.
Timer Yönetimi: Günlük Kullanım Komutları
Timer’larla çalışırken en sık kullandığım komutlar şunlar:
# Sistemdeki tüm timer'ları listele
systemctl list-timers --all
# Belirli bir timer'ın durumunu kontrol et
systemctl status yedekleme.timer
# Timer'ı başlat
systemctl start yedekleme.timer
# Timer'ı durdur
systemctl stop yedekleme.timer
# Timer'ı yeniden başlat
systemctl restart yedekleme.timer
# Timer'ı devre dışı bırak
systemctl disable yedekleme.timer
# Servis dosyasını değiştirdikten sonra
systemctl daemon-reload
# Timer'ı beklemeden hemen tetikle (test için)
systemctl start yedekleme.service
systemctl list-timers --all komutu özellikle çok güzel. Tüm timer’ları, son çalışma zamanlarını ve bir sonraki tetiklenme zamanlarını güzel bir tablo halinde gösterir.
Log Takibi: Journald Entegrasyonu
Cron’un en can sıkıcı yanlarından biri log yönetimiydi. Systemd timer’larda tüm çıktılar otomatik olarak journald’a gider.
# Belirli bir servis için logları görüntüle
journalctl -u yedekleme.service
# Canlı log takibi
journalctl -u yedekleme.service -f
# Son 50 satır
journalctl -u yedekleme.service -n 50
# Son 24 saatin logları
journalctl -u yedekleme.service --since "24 hours ago"
# Sadece hataları göster
journalctl -u yedekleme.service -p err
# Timer ve servis loglarını birlikte göster
journalctl -u yedekleme.timer -u yedekleme.service
Bir iş neden çalışmadı? Hata ne verdi? Kaç saniye sürdü? Bunların hepsine journald üzerinden ulaşabilirsiniz. Cron günlerine kıyasla bu gerçekten büyük bir lüks.
Gerçek Dünya Senaryosu 3: Kullanıcı Seviyesi Timer
Sistemdeki her kullanıcı, root yetkisi gerektirmeden kendi timer’larını oluşturabilir. Bu özellikle geliştirici ortamlarında çok işe yarıyor.
Diyelim ki bir geliştirici her 30 dakikada bir kendi proje dizinini bir uzak sunucuya senkronize etmek istiyor:
# ~/.config/systemd/user/ dizinini oluştur
mkdir -p ~/.config/systemd/user/
# ~/.config/systemd/user/proje-sync.service
[Unit]
Description=Proje Dizini Senkronizasyon
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/rsync -avz --delete ~/projeler/ kullanici@sunucu:/yedek/projeler/
StandardOutput=journal
StandardError=journal
# ~/.config/systemd/user/proje-sync.timer
[Unit]
Description=Her 30 Dakikada Proje Sync
[Timer]
OnCalendar=*:0/30
Persistent=true
[Install]
WantedBy=default.target
Kullanıcı timer’larını yönetmek için --user flag’ini ekleyin:
systemctl --user daemon-reload
systemctl --user enable --now proje-sync.timer
systemctl --user list-timers
journalctl --user -u proje-sync.service -f
Kullanıcı oturumu kapatıldığında timer’ların çalışmaya devam etmesi için:
loginctl enable-linger kullanici_adi
Gelişmiş Özellikler
AccuracySec
Systemd timer’lar varsayılan olarak 1 dakikalık bir doğruluk toleransına sahiptir. Bu, sistem kaynaklarını optimize etmek için birden fazla timer’ı birbirine yakın zamanlarda gruplandırmasına olanak sağlar. Eğer kesin zamanlama istiyorsanız:
[Timer]
OnCalendar=*-*-* 02:00:00
AccuracySec=1s
AccuracySec=1s ile saniye hassasiyetinde zamanlama elde edersiniz. Ama gereksiz yere bu değeri düşük tutmak sistem kaynaklarını boşa tüketir.
WakeSystem
Sistem suspend modundayken bile bir timer’ı çalıştırmanız gerekiyorsa:
[Timer]
OnCalendar=*-*-* 04:00:00
WakeSystem=true
Bu direktif, timer zamanı geldiğinde sistemi uyku modundan çıkarır. Laptop’larda enerji tasarrufu kritik olduğunda ama yine de belirli görevlerin çalışması gerektiğinde çok işe yarar.
Unit Direktifi
Varsayılan olarak bir timer, aynı isimli .service dosyasını çalıştırır. Ama farklı bir servis dosyasına da bağlayabilirsiniz:
[Timer]
OnCalendar=daily
Unit=ozel-isim.service
Cron ile Karşılaştırma: Ne Zaman Hangisini Kullanmalı?
Her ne kadar systemd timer’lar cron’a kıyasla çok daha güçlü olsa da cron’un hala tercih edilebileceği durumlar var.
Systemd timer tercih edin:
- Servis bağımlılıklarını yönetmeniz gerektiğinde
- Detaylı log ve hata takibi önemliyse
- Sistem başlangıcına göre göreceli zamanlama istiyorsanız
- Çakışan çalışmaları engellemek istiyorsanız
systemctlile merkezi yönetim istiyorsanız- Güvenilir yeniden deneme ve hata yönetimi gerekiyorsa
Cron tercih edebilirsiniz:
- Çok basit, tek seferlik görevler için
- Systemd olmayan sistemlerde (eski sunucular, bazı konteynerler)
- Mevcut cron tabanlı otomasyon altyapısıyla uyumluluk gerektiğinde
- Başka bir sysadmin’in kolayca anlayabileceği son derece basit yapılar için
Pratikte yeni projelerde ve modern sistemlerde systemd timer’ı tercih ediyorum. Eski sistemleri taşırken ise cron’u olduğu gibi bırakıp yeni görevleri timer ile yazıyorum.
Sorun Giderme İpuçları
Timer çalışmıyor mu? Şu adımları takip edin:
# 1. Timer'ın aktif olduğunu doğrula
systemctl status yedekleme.timer
# 2. Timer dosyasında sözdizim hatası var mı?
systemd-analyze verify yedekleme.timer
# 3. Servisin manuel çalıştığını doğrula
systemctl start yedekleme.service
systemctl status yedekleme.service
# 4. Son çalışma loglarına bak
journalctl -u yedekleme.service --since "1 hour ago"
# 5. Systemd genel loglarına bak
journalctl -xe | grep yedekleme
# 6. Zaman ifadesinin doğruluğunu kontrol et
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
Sık karşılaştığım bir sorun da daemon-reload unutmak. Dosyayı değiştirip timer’ı başlattığınızda eski konfigürasyon hala bellekte olabilir. Her değişiklikten sonra systemctl daemon-reload çalıştırmayı alışkanlık haline getirin.
Mevcut Cron İşlerini Timer’a Dönüştürme
Elinizde crontab girişleri varsa bunları nasıl dönüştüreceğinizi görelim. Örnek bir crontab:
# Her gece saat 2'de yedek al
0 2 * * * /usr/local/bin/yedek.sh
# Her 5 dakikada bir kontrol et
*/5 * * * * /usr/local/bin/kontrol.sh
# Her Pazartesi sabah 8'de rapor gönder
0 8 * * 1 /usr/local/bin/rapor-gonder.sh
Bunların systemd karşılıkları:
# 0 2 * * * --> OnCalendar=*-*-* 02:00:00
# */5 * * * * --> OnCalendar=*:0/5
# 0 8 * * 1 --> OnCalendar=Mon *-*-* 08:00:00
Dönüşüm mantığını bir kez kavradıktan sonra bu iş çok kolay hale geliyor.
Sonuç
Systemd timer’lar, cron’un onlarca yıllık hâkimiyetini sona erdiren ve gerçekten ihtiyaç duyduğumuz özellikleri getiren bir çözüm. Journald entegrasyonu sayesinde “bu iş neden çalışmadı?” sorusunun cevabına saniyeler içinde ulaşabiliyorsunuz. Bağımlılık yönetimi ile “PostgreSQL henüz hazır değil, o yüzden yedekleme başlamasın” diyebiliyorsunuz. Persistent direktifi ile “sistem kapalıyken kaçırdığım görevleri açılışta çalıştır” özelliğine sahip oluyorsunuz.
Eğer hala tüm zamanlama görevleriniz için cron kullanıyorsanız, bir sonraki yeni görevinizi systemd timer olarak yazmayı deneyin. Farkı ilk günlük log takibinde anlayacaksınız. Bir kez alıştıktan sonra cron’a dönmek istemeyeceksiniz, bunu garanti ederim.
Modern Linux sistemlerinde systemd zaten var. Ekstra bir bağımlılık yok, ekstra bir kurulum yok. Sadece iki dosya yazıyorsunuz ve sisteminizin en güçlü araçlarından birini kullanıma açıyorsunuz. Bu kadar basit.