Cron job, Linux sunucuda belirlediğiniz takvime göre otomatik çalışan bir komut ya da betiktir. Zamanlanmış görev mantığı basittir: cron servisi her dakika başında görev listesini okur, o dakikaya denk gelen satırları bulur ve çalıştırır. Sunucu açık olduğu sürece bu döngü hiç durmaz.
Yedek alma, log dosyalarını temizleme, sunucuyu gece belirli bir saatte yeniden başlatma, bir API’den veri çekme — tekrar eden ne varsa cron’a devredilir. Bu sayfada crontab söz dizimini alan alan açıklayacak, sık kullanılan zaman ifadelerini tablo hâlinde vereceğiz. Ama asıl kıymetli kısım sonlarda: cron’da çalışmayan bir görevi nasıl bulacağınız. Destek taleplerinde gördüğümüz cron sorunlarının büyük çoğunluğu söz dizimi hatası değil, ortam değişkeni sorunudur.
Cron Nedir ve Nasıl Çalışır?#
Cron, arka planda sürekli çalışan bir servistir (daemon). Görevi tek cümlede özetlenebilir: saati takip eder, listedeki satırların zaman ifadesiyle o anı karşılaştırır, eşleşen satırı çalıştırır. Çözünürlüğü bir dakikadır — yani “her 30 saniyede bir” diye bir cron ifadesi yoktur.
Görev tanımları birkaç ayrı yerde tutulur ve hepsi aynı anda geçerlidir:
- Kullanıcı crontab’ı
crontab -eile düzenlenir, görev o kullanıcının yetkileriyle çalışır. Günlük kullanımda en çok tercih edilen yer burasıdır.- /etc/crontab
- Sistem geneli görev dosyası. Beş zaman alanından sonra kullanıcı adı sütunu vardır.
- /etc/cron.d/
- Paketlerin kendi görev dosyalarını bıraktığı klasör. Biçimi
/etc/crontabile aynıdır; kendi görevlerinizi ayrı dosyalarda tutmak için de kullanışlıdır. - /etc/cron.daily, cron.hourly, cron.weekly
- İçine attığınız çalıştırılabilir betiği belirtilen sıklıkta çalıştıran hazır klasörler. Saat hassasiyeti gerekmeyen işler için pratiktir.
Servisin ayakta olup olmadığını kontrol etmek ilk iştir. Bazı minimal sunucu imajlarında cron paketi hiç kurulu gelmez:
# Servis çalışıyor mu?
systemctl status cron # Ubuntu / Debian
systemctl status crond # AlmaLinux / Rocky
# Kurulu değilse
sudo apt install -y cron # Ubuntu / Debian
sudo dnf install -y cronie # AlmaLinux / Rocky
# Açılışta otomatik başlasın
sudo systemctl enable --now cron
# Sunucunun saat dilimi ne? (cron sistem saatini kullanır)
timedatectl
# Time zone: Europe/Istanbul (+03, +0300)
timedatectl çıktısına bakın, gerekiyorsa sudo timedatectl set-timezone Europe/Istanbul ile düzeltin.crontab -e ile İlk Görevinizi Oluşturma#
Kendi kullanıcınızın görev listesini açmak için tek komut yeterli:
# Görev listesini düzenle (ilk seferde düzenleyici seçimi sorar, nano önerilir)
crontab -e
# Mevcut görevleri listele
crontab -l
# Başka bir kullanıcının listesini düzenle (root gerekir)
sudo crontab -u minecraft -e
# Listeyi bir dosyaya yedekle — düzenlemeden önce yapılması iyi bir alışkanlık
crontab -l > ~/crontab-yedek-$(date +%F).txt
# TÜM görevleri sil (dikkatli olun, onay sormaz)
crontab -r
Açılan dosyaya her satıra bir görev yazılır. Satır sonunda kaydedip çıktığınızda cron installing new crontab mesajını basar; bu mesajı görmediyseniz kayıt olmamıştır. Dosyanın en sonunda mutlaka boş bir satır bırakın — bazı cron sürümleri son satırı yok sayar.
# Yorum satırları # ile başlar, açıklama yazmak çok işe yarar
# Her gece 04:00'te yedek betiğini çalıştır
0 4 * * * /opt/betikler/yedek.sh >> /var/log/yedek.log 2>&1
# Her 10 dakikada bir disk kullanımını kaydet
*/10 * * * * /usr/bin/df -h >> /var/log/disk.log 2>&1
# Her Pazartesi 03:30'da 30 günden eski logları sil
30 3 * * 1 /usr/bin/find /var/log/uygulama -name "*.log" -mtime +30 -delete
Beş Alanın Anlamı: Cron Zaman Söz Dizimi#
Bir cron satırının başındaki beş alan, görevin ne zaman çalışacağını belirler. Sıra hiç değişmez ve alanlar boşlukla ayrılır:
┌───────────── dakika (0 - 59)
│ ┌─────────── saat (0 - 23)
│ │ ┌───────── ayın günü (1 - 31)
│ │ │ ┌─────── ay (1 - 12)
│ │ │ │ ┌───── haftanın günü (0 - 7, hem 0 hem 7 = Pazar)
│ │ │ │ │
* * * * * calistirilacak-komut
| Alan | Sıra | Geçerli değerler | Not |
|---|---|---|---|
| Dakika | 1. | 0-59 | En sık kullanılan alan |
| Saat | 2. | 0-23 | 24 saat biçimi; sunucu saat dilimine göre |
| Ayın günü | 3. | 1-31 | Ay sonu farkına dikkat (31 her ayda yok) |
| Ay | 4. | 1-12 | jan, feb gibi kısaltmalar da geçerli |
| Haftanın günü | 5. | 0-7 | 0 ve 7 Pazar, 1 Pazartesi; mon-sun yazılabilir |
Yıldız, aralık, liste ve adım#
Her alana dört tip değer yazabilirsiniz. Bunları öğrendiğinizde crontab’ın tamamını öğrenmiş olursunuz:
*(yıldız) — “her değer”. Saat alanında yıldız varsa görev 24 saatin hepsinde çalışır.-(aralık) —1-5, haftanın günü alanında Pazartesi’den Cuma’ya demektir. Saat alanında9-18, mesai saatleri.,(liste) —6,18, yalnızca o iki değerde çalışır. Aralık ve listeyi birlikte kullanabilirsiniz:0,30 8-20 * * *./(adım) —*/5, “sıfırdan başlayarak beşer beşer”. Aralıkla birleşince daha okunur olur:0-30/10, ilk yarım saatte on dakikada bir.
Sık kullanılan zaman ifadeleri#
| İfade | Ne zaman çalışır | Tipik kullanım |
|---|---|---|
* * * * * | Her dakika | Yalnızca test için |
*/5 * * * * | Her 5 dakikada bir | Servis canlılık kontrolü |
*/15 * * * * | Çeyrek saatte bir | Kaynak kullanımı kaydı |
0 * * * * | Her saat başı (xx:00) | Ara yedek, önbellek temizliği |
0 4 * * * | Her gün 04:00 | Günlük tam yedek |
30 3 * * 0 | Her Pazar 03:30 | Haftalık bakım |
0 2 * * 1-5 | Hafta içi her gün 02:00 | İş günü raporu |
0 6,18 * * * | Her gün 06:00 ve 18:00 | Günde iki kez senkronizasyon |
0 0 1 * * | Her ayın 1’i gece yarısı | Aylık arşiv |
*/10 9-18 * * 1-5 | Hafta içi 09:00-18:59 arası 10 dakikada bir | Mesai saati izleme |
0 5 */3 * * | Üç günde bir 05:00 | Seyrek bakım işleri |
@reboot | Sunucu her açıldığında | Servis başlatma |
0 4 1 * 1 satırı “ayın 1’i ve Pazartesi” değil, “ayın 1’i veya Pazartesi” demektir. Çoğu kişinin beklemediği bir davranıştır; ikisini aynı anda kısıtlamanız gerekiyorsa kontrolü betiğin içinde yapın.@reboot, @daily ve Diğer Kısayollar#
Beş alan yazmak istemediğiniz durumlar için hazır kısayollar var. Okunurluğu artırırlar, özellikle @reboot başka türlü yazılamaz:
@reboot /opt/betikler/baslat.sh # sunucu her açıldığında (cron başladığında)
@yearly /opt/betikler/arsiv.sh # yılda bir, 1 Ocak 00:00 ( = 0 0 1 1 * )
@monthly /opt/betikler/aylik.sh # ayın 1'i 00:00 ( = 0 0 1 * * )
@weekly /opt/betikler/haftalik.sh # her Pazar 00:00 ( = 0 0 * * 0 )
@daily /opt/betikler/gunluk.sh # her gün 00:00 ( = 0 0 * * * )
@hourly /opt/betikler/saatlik.sh # her saat başı ( = 0 * * * * )
@reboot görevleri, cron servisi başladığı anda çalışır — sunucunun açılış sürecinin en sonunda değil. Ağ ya da veritabanı henüz hazır olmayabilir. Bu yüzden bir oyun sunucusunu ya da bota benzer sürekli çalışan bir uygulamayı @reboot ile başlatmak yerine systemd servisi olarak tanımlamak çok daha sağlıklıdır: systemd bağımlılıkları bekler, çöken süreci otomatik yeniden başlatır.
Kısayolların bir zayıf tarafı daha var: @daily ve @weekly hep gece yarısına denk gelir. Aynı sunucudaki birkaç görev aynı anda tetiklenirse disk ve CPU aynı dakikada zirve yapar. Görevleri elle 03:10, 03:40 gibi farklı dakikalara dağıtmak daha iyi sonuç verir.
Kullanıcı crontab’ı, /etc/crontab ve /etc/cron.d#
İki biçim arasındaki tek fark, fazladan bir sütun. Ama o sütun unutulduğunda görev hiç çalışmaz ve neden çalışmadığı da kolay anlaşılmaz:
# --- KULLANICI crontab'ı (crontab -e ile açılır) ---
# dakika saat gün ay haftagünü KOMUT
0 4 * * * /opt/betikler/yedek.sh
# --- SİSTEM crontab'ı (/etc/crontab veya /etc/cron.d/dosya) ---
# dakika saat gün ay haftagünü KULLANICI KOMUT
0 4 * * * minecraft /opt/betikler/yedek.sh
Hangisini seçmeli? Pratik kural şu: görev tek bir kullanıcıya aitse ve o kullanıcının yetkileriyle çalışacaksa kullanıcı crontab’ı yeterlidir. Sunucu genelini ilgilendiren, sistem yönetimine ait bir işse (log döndürme, güvenlik taraması) /etc/cron.d/ altında ayrı bir dosya açmak daha düzenlidir; çünkü o dosyayı yedekleyebilir, sürüm kontrolüne koyabilir, silerek görevi tek hamlede kaldırabilirsiniz.
/etc/cron.d/ içindeki dosya adlarında nokta olmamalıdır. yedek.cron adlı dosya sessizce yok sayılır, yedek-gorevi çalışır. Aynı klasördeki dosyaların izni 644, sahibi root olmalıdır. Bu kural bir sürü kişiyi saatlerce uğraştırmıştır.PATH ve Ortam Değişkeni Tuzağı#
Cron sorunlarının en sık sebebi burada. Terminalde elinizle çalıştırdığınızda mükemmel çalışan bir betik, cron’a koyduğunuz anda hiçbir şey yapmıyorsa neredeyse kesin olarak PATH sorunudur.
Sebep şu: SSH ile oturum açtığınızda kabuk, /etc/profile ve ~/.bashrc gibi dosyaları okur; PATH’iniz zenginleşir, JAVA_HOME gibi değişkenler tanımlanır. Cron bu dosyaların hiçbirini okumaz. Elinde yalnızca çok kısa bir PATH ve neredeyse boş bir ortam vardır.
# Cron'un gerçekten ne gördüğünü öğrenmenin en kesin yolu:
# geçici bir görev ekleyip ortamı dosyaya döktürün
* * * * * /usr/bin/env > /tmp/cron-ortam.txt 2>&1
# Bir dakika bekleyip bakın:
cat /tmp/cron-ortam.txt
# HOME=/home/yonetici
# LOGNAME=yonetici
# PATH=/usr/bin:/bin <-- terminaldekiyle aynı DEĞİL
# SHELL=/bin/sh <-- bash değil, sh
# Terminaldeki PATH ile karşılaştırın:
echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Farkı gördükten sonra çözüm üç yoldan biri:
-
Komutların tam yolunu yazın#
En sağlam yöntem budur.
dockerdeğil/usr/bin/docker,javadeğil/usr/lib/jvm/temurin-25-jdk/bin/java. Bir komutun tam yolunuwhich dockerya dacommand -v javaile bulursunuz. -
crontab’ın en üstüne PATH tanımlayın#
SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin MAILTO="" 0 4 * * * /opt/betikler/yedek.sh >> /var/log/yedek.log 2>&1 -
Betiğin kendi ortamını kurmasını sağlayın#
Betiğin ilk satırlarına gereken değişkenleri açıkça yazın. Bu, betiği cron’dan bağımsız hâle getirir; başka bir zamanlayıcıya taşırsanız da çalışmaya devam eder.
#!/bin/bash set -euo pipefail export PATH=/usr/local/bin:/usr/bin:/bin export JAVA_HOME=/usr/lib/jvm/temurin-25-jdk cd /opt/minecraft || exit 1 # ... asıl işler
İkinci en sık sebep çok daha basit: betiğe çalıştırma izni verilmemiş olması. chmod +x /opt/betikler/yedek.sh komutu bunu düzeltir. Üçüncüsü, betiğin göreli yol kullanması — cron görevleri kullanıcının ev dizininde başlar, betiğin bulunduğu klasörde değil. Betiğin başına cd koyun. Bu ve benzeri komutların ayrıntısı Linux temel komutları sayfasında.
Çıktıyı Yakalamak ve Görevleri Kilitlemek#
Log dosyasına yönlendirme#
Yönlendirme yapmazsanız cron, görevin çıktısını kullanıcının yerel postasına gönderir. Çoğu sunucuda posta sistemi yapılandırılmamıştır, yani çıktı kaybolur. Hata mesajı da kaybolur. Sonuç: neyin ters gittiğini asla göremezsiniz.
# Hem normal çıktıyı hem hatayı aynı dosyaya EKLE
0 4 * * * /opt/betikler/yedek.sh >> /var/log/yedek.log 2>&1
# Sadece hataları kaydet, normal çıktıyı at
0 4 * * * /opt/betikler/yedek.sh > /dev/null 2>> /var/log/yedek-hata.log
# Hiçbir çıktı istemiyorum (dikkat: hataları da göremezsiniz)
0 4 * * * /opt/betikler/yedek.sh > /dev/null 2>&1
# Her çalıştırmaya tarih damgası düşen daha okunur bir kalıp
0 4 * * * echo "[$(date '+\%F \%T')] baslodi" >> /var/log/yedek.log; /opt/betikler/yedek.sh >> /var/log/yedek.log 2>&1
% işareti özel anlamlıdır: cron onu satır sonu olarak yorumlar ve komutu ortadan böler. date biçim dizgisi gibi yüzde içeren her yerde \% diye kaçırmanız gerekir. Bu kuralı bilmeyenler saatlerce “komut neden yarım çalışıyor” diye arar. En temizi: karmaşık komutu bir .sh dosyasına taşımak ve crontab’dan yalnızca o dosyayı çağırmak.Log dosyası zamanla büyür. /etc/logrotate.d/ altına küçük bir kural koyup dosyayı haftalık döndürmek iyi bir alışkanlıktır; aksi hâlde bir gün diskin dolduğunu fark edersiniz.
flock ile üst üste binmeyi engelleme#
Şöyle bir senaryo: beş dakikada bir çalışan bir yedekleme görevi var. Normalde iki dakika sürüyor. Dünya dosyası büyüdükçe süre sekiz dakikaya çıkıyor. Cron bunu bilmez — beşinci dakikada ikinci kopyayı başlatır, onuncu dakikada üçüncüyü. Kısa sürede diskte aynı işi yapan üç süreç birden döner, I/O tıkanır, sunucu yanıt vermez hâle gelir.
Çözüm tek satır:
# -n : kilit alınamıyorsa BEKLEME, hemen çık
*/5 * * * * /usr/bin/flock -n /tmp/yedek.lock /opt/betikler/yedek.sh >> /var/log/yedek.log 2>&1
# Kilit için en fazla 60 saniye bekle, alamazsan çık
*/5 * * * * /usr/bin/flock -w 60 /tmp/yedek.lock /opt/betikler/yedek.sh
# Karmaşık komutu kabuk üzerinden çalıştırmak gerekirse -c kullanın
0 * * * * /usr/bin/flock -n /tmp/rsync.lock -c "/usr/bin/rsync -az /opt/minecraft/ /yedek/minecraft/"
# Betiğin kendi içinden kilitlenmesi (harici crontab değişikliği gerekmez)
#!/bin/bash
exec 9>/tmp/gorev.lock
flock -n 9 || { echo "Onceki calisma surüyor, cikiliyor"; exit 0; }
# ... asıl işler
Yedekleme görevlerinde bu satır neredeyse zorunludur. Doğru yedekleme düzeninin tamamı için 3-2-1 yedekleme kuralı sayfasına, Minecraft’a özel bir örnek için cron ile otomatik Minecraft yedekleme sayfasına bakabilirsiniz.
Cron Çalışmıyorsa: Hata Ayıklama Sırası#
Sırayla ilerleyin; alt sıradaki adımlara geçmeden önce üsttekini gerçekten doğrulayın. Bu sıra, sorunu bulma süresini dakikalara indirir.
-
Servis çalışıyor mu?#
systemctl status cron # Active: active (running) yazmalı -
Görev gerçekten kayıtlı mı?#
crontab -lçıktısında satırınız görünüyor mu? Yanlış kullanıcının crontab’ına yazmış olmak çok yaygın bir hatadır —sudo crontab -eilecrontab -efarklı listeleri açar. -
Cron görevi tetiklemiş mi?#
# Ubuntu / Debian sudo journalctl -u cron --since "1 hour ago" sudo grep CRON /var/log/syslog | tail -20 # AlmaLinux / Rocky sudo journalctl -u crond --since "1 hour ago" sudo tail -20 /var/log/cronSatırda görevinizin adı geçiyorsa cron işini yapmış demektir; sorun betiğin içindedir. Hiç geçmiyorsa zaman ifadesi ya da dosya biçimi hatalıdır.
-
Betik elle çalışıyor mu?#
# Kendi kullanıcınızla değil, görevin çalışacağı kullanıcıyla deneyin sudo -u minecraft /opt/betikler/yedek.sh echo $? # 0 ise başarılı, değilse hata var -
Ortamı boşaltıp deneyin#
Betik elle çalışıp cron’da çalışmıyorsa, cron ortamını taklit edin. Aşağıdaki komut PATH’i temizler ve ortam değişkenlerini yok sayar; burada da patlıyorsa sorun kesinlikle PATH’tir.
env -i /bin/sh -c "/opt/betikler/yedek.sh" -
İzinleri ve satır sonlarını kontrol edin#
ls -lile çalıştırma izni (x) var mı bakın. Betiği Windows’ta düzenlediyseniz satır sonlarıCRLFolmuş olabilir; bu durumdabad interpreterhatası alırsınız.sed -i 's/\r$//' betik.shdüzeltir.
Log okuma alışkanlığı kazanmak bu işin yarısıdır. Sunucunuzun genel sağlığını izlemek için sunucu izleme ve monitoring rehberine, oyun sunucusu loglarını çözümlemek için sunucu logları nasıl okunur sayfasına bakın.
systemd Timer: Cron’a Alternatif#
Modern Linux dağıtımlarında cron’un yerini yavaş yavaş systemd timer’ları alıyor. Cron ölmedi, hala her yerde kullanılıyor — ama bazı işlerde timer belirgin biçimde daha iyi.
| Özellik | cron | systemd timer |
|---|---|---|
| Yazma kolaylığı | Tek satır | İki dosya (.service + .timer) |
| Kaçırılan görevi telafi | Yok | Persistent=true |
| Günlük kaydı | Elle yönlendirme gerekir | journalctl otomatik |
| Bağımlılık yönetimi | Yok | After=network.target |
| Kaynak sınırlama | Yok | CPU ve bellek kotası |
| Rastgele gecikme | Yok | RandomizedDelaySec |
# /etc/systemd/system/yedek.service
[Unit]
Description=Gunluk yedekleme gorevi
[Service]
Type=oneshot
User=minecraft
ExecStart=/opt/betikler/yedek.sh
# /etc/systemd/system/yedek.timer
[Unit]
Description=Yedeklemeyi her gun 04:00'te calistir
[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
# Etkinleştir ve başlat
sudo systemctl daemon-reload
sudo systemctl enable --now yedek.timer
# Sıradaki çalışma zamanlarını gör
systemctl list-timers --all
# Görevin çıktısını oku (yönlendirme gerekmez)
journalctl -u yedek.service -n 50
# Zamanını beklemeden hemen test et
sudo systemctl start yedek.service
Karar kuralı basit: birkaç dakikada bir çalışan kısa ve basit işlerde cron kalsın. Sunucu kapalıyken kaçırılmaması gereken, uzun süren, çıktısı izlenmesi gereken ya da başka bir servise bağımlı işlerde timer’a geçin. Otomatik yeniden başlatma gibi senaryolarda ikisi birlikte de kullanılabilir.
Cron Görevlerinde Güvenlik ve Kaynak Dengesi#
Zamanlanmış görevler sessizce çalıştığı için gözden kaçarlar. Birkaç noktaya dikkat etmekte fayda var.
Görevleri mümkün olan en düşük yetkiyle çalıştırın. Bir yedekleme betiğinin root olması çoğu zaman gerekmez; ilgili klasörlerin sahibi olan kullanıcı yeterlidir. Root ile çalışan bir cron görevi, betiğin yazma izni olan herkese fiilen root yetkisi verir. Betik dosyasının izni 755, sahibi doğru kullanıcı olmalı; içinde parola varsa dosya izni 600 yapılmalı ve mümkünse parola ayrı bir ortam dosyasında tutulmalıdır.
Kaynak tarafında ise şu var: ağır görevleri oyuncuların en yoğun olduğu saatlere koymayın. Bir yedekleme sırasında disk okuma yazma yükü zirveye çıkar ve TPS düşüşü olarak oyuncuya yansır. Türkiye’ye hizmet veren bir oyun sunucusunda en sakin saat aralığı genellikle 04:00-07:00’dir. NVMe SSD üzerinde çalışan bir sunucuda bu etki çok daha küçüktür; yine de yoğun saati seçmenin bir anlamı yok. Görevleri farklı dakikalara dağıtmak (04:00, 04:20, 04:40) tek bir zirve yerine üç küçük dalga oluşturur.
Zamanlanmış görevlerini rahatça çalıştırabileceğiniz, kök erişimli bir makineye ihtiyacınız varsa VDS paketlerine bakabilirsiniz. Kurduğunuz bir görev bir türlü tetiklenmiyorsa crontab satırınızı ve log çıktısını ekleyerek destek talebi açmanız en hızlı yol olur.
Özetle#
Cron, Linux sunucuda tekrar eden işleri devrettiğiniz en basit ve en yaygın araçtır. Öğrenilmesi gereken tek şey beş alanlık zaman söz dizimidir: dakika, saat, ayın günü, ay, haftanın günü. */5 * * * * her beş dakikada bir, 0 4 * * * her gece dörtte demektir; gerisi bu iki kalıbın türevidir.
Asıl kritik nokta söz dizimi değil, çalışma ortamı. Cron’un PATH’i kısıtlıdır, kabuk dosyalarınızı okumaz ve çıktıyı yönlendirmezseniz hata mesajı yok olur. Bu yüzden üç alışkanlık edinin: komutların tam yolunu yazın, her görevi bir log dosyasına yönlendirin, uzun süren işleri flock ile kilitleyin. Bu üçü, cron kaynaklı sorunların neredeyse tamamını baştan engeller.
Görev karmaşıklaşıyor, kaçırılmaması gerekiyor ya da başka servislere bağımlıysa systemd timer’a geçmekten çekinmeyin. Cron’un yerini almak zorunda değil; ikisi aynı sunucuda yan yana rahatça yaşar.