n8n Zamanlama Tetikleyicisi Yanlış Saatte Çalışıyor
n8n zamanlama tetikleyicisi neden yanlış saatte çalışır? TZ, GENERIC_TIMEZONE ve iş akışı ayarlarını doğru yapılandırarak saat farkı sorununu kalıcı olarak çözebilirsiniz.
n8n zamanlama tetikleyicinizin neden yanlış saatte çalıştığı
Bir n8n zamanlama tetikleyicisi yanlış saatte çalışır çünkü n8n saat dilimi bilgisini üç farklı yerden okur ve bunlardan yalnızca birini düzeltmek sorunun sadece bir kısmını çözer. Bu üç yer; container'ın kendi TZ değişkeni, instance varsayılanı olan GENERIC_TIMEZONE ve her bir iş akışı (workflow) içinde ayarlanan saat dilimidir. Üçünü de bir kez ayarladığınızda, sonrasında oluşturacağınız tüm zamanlamalar beklediğiniz saatte çalışır.
Önce yaygın bir yanlış kanıyı düzeltelim. Yeni kurulmuş bir self-hosted n8n, zamanlamayı UTC (Eşgüdümlü Evrensel Zaman) bazlı yapmaz. Resmi imaj herhangi bir TZ ayarı içermediğinden, container saati UTC'dir. Zamanlama ise ayrı bir katmandır ve n8n'in GENERIC_TIMEZONE için dokümante edilmiş varsayılan değeri America/New_York'dir (Ağustos 2026 itibarıyla). Bu nedenle, dokunulmamış bir instance, Zamanlama Tetikleyicilerini (Schedule Triggers) New York saatine göre çalıştırır. Kullanıcıların bildirdiği saat farkının kendi UTC konumlarıyla nadiren eşleşmesinin nedeni budur. Berlin'deki bir kullanıcı 06:00 için zamanlama kurduğunda yerel saatle 12:00'de tetikleme alır; Amerika Birleşik Devletleri'nin yaz saati uygulamasına geçtiği ancak Avrupa'nın henüz geçmediği Mart haftalarında ise bu saat 11:00 olur.
Üç saat dilimi katmanı ve hangisinin öncelikli olduğu
TZ, container içindeki işletim sistemi saat dilimidir. n8n belgeleri bunu, date gibi betiklerin ve komutların ne döndüreceğini kontrol etmek için sistem saat dilimini ayarlayan değişken olarak tanımlar. Bu değişken, container içinde date komutunun ne yazdıracağına, container günlük satırlarına hangi zaman damgasının düşeceğine, bir Code düğümünde new Date() fonksiyonunun ne döndüreceğine ve orada çalıştırdığınız herhangi bir shell betiğinin ne göreceğine karar verir. Bir Schedule Trigger tetikleyicisinin ne zaman çalışacağı üzerinde hiçbir etkisi yoktur.
GENERIC_TIMEZONE, n8n örneği (instance) saat dilimidir. Belgeler bunu n8n örneği saat dilimi olarak adlandırır ve Cron gibi zamanlama düğümleri için önemli olduğunu belirtir. Buradaki Cron, standart zamana dayalı zamanlama sözdizimini ifade eder ve n8n bunu Schedule Trigger üzerinde Custom (Cron) seçeneği olarak sunar.
İş akışı saat dilimi, her iş akışı için ayrı ayrı ayarlanır. İş akışını tuval üzerinde açın, sağ üst köşedeki üç noktayı seçin, Settings kısmına girin ve ardından Timezone değerini değiştirin. Bu ayar, ilgili iş akışı için GENERIC_TIMEZONE değerini geçersiz kılar.
Schedule Trigger için öncelik sırası sabittir. n8n, eğer tanımlanmışsa iş akışı saat dilimini, tanımlanmamışsa GENERIC_TIMEZONE üzerinden alınan örnek saat dilimini, o da yoksa America/New_York olan yerleşik varsayılan değeri kullanır. TZ, bu karar sürecinin hiçbir aşamasında dikkate alınmaz.
Düğümleriniz içindeki tarihler için cevap, kodun hangi saate sorduğuna bağlıdır. n8n ifadelerinin arkasındaki tarih kütüphanesi olan Luxon, n8n saat dilimini kullanır; bu nedenle $now ve $today, tetikleyicide olduğu gibi aynı iş akışı-ardından-örnek öncelik sırasını izler. Bir Code düğümündeki düz JavaScript new Date() ise işletim sistemine sorar, bu yüzden TZ değerini takip eder. Bu ayrım, buradaki kafa karışıklığının çoğunun kaynağıdır: tetikleyici doğru çalışırken, iş akışının yazdığı her zaman damgası saatlerce sapmış olabilir.
Üçünü de Compose dosyasında ayarlayın
Kimsenin birini ayarlayıp diğerini unutmaması için TZ ve GENERIC_TIMEZONE ifadelerini dosyada yan yana getirin. Aşağıdaki parça, çalışan bir servisin saat dilimiyle ilgili kısmıdır. Dosyanın geri kalanı, reverse proxy ve sertifika, HTTPS arkasında self-hosted n8n rehberinden alınmıştır.
services:
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- GENERIC_TIMEZONE=Europe/Berlin
- TZ=Europe/Berlin
- N8N_RUNNERS_ENABLED=true
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:Bunu docker compose restart ile değil, docker compose up -d ile uygulayın. Bir yeniden başlatma işlemi, aynı container'ı oluşturulduğu ortam değişkenleriyle tekrar başlatır; bu nedenle dosya değişiklikleri çalışan sürece yansımaz. up -d ise değişen ortamı algılar ve container'ı yeniden oluşturur. Eğer bu değerleri satır içi yerine bir env dosyasında tutuyorsanız, aynı yeniden oluşturma kuralı geçerlidir ve Compose env dosyası ve secret yönetimi rehberi, bu dosyanın nereden okunduğunu açıklamaktadır.
Region/City biçiminde, Europe/Berlin veya America/Sao_Paulo gibi bir IANA (Internet Assigned Numbers Authority) bölge adı kullanın. Bu isimler ilgili yerin yaz saati uygulaması kurallarını içerir, bu sayede yerel saatler değiştiğinde fark da değişir. Etc/GMT+5 gibi sabit farka sahip isimler mevsimlerle değişmez ve işaretleri tahmin edeceğinizin tersidir. LC_ALL=C TZ=Etc/GMT+5 date +%z komutunu çalıştırdığınızda -0500 çıktısını verir. Bu isimlerden kaçının.
Neden sadece birini ayarlamak sorunu kısmen çözer
Yalnızca GENERIC_TIMEZONE ayarlandığında, Zamanlama Tetikleyicisi (Schedule Trigger) istediğiniz saatte çalışır ancak işletim sistemini okuyan her şey UTC zaman diliminde kalır. new Date().toString() çağıran bir Kod düğümü (Code node) UTC dizgisi döndürür, container günlük satırları UTC damgası taşır ve sistem saatinden oluşturulan tüm dosya adları yanlış gece yarısında değişir.
Yalnızca TZ ayarlandığında ise tam tersi gerçekleşir. docker compose exec n8n date yerel saatinizi yazdırır ve bu durum başarılı gibi görünür; ancak Zamanlama Tetikleyicisi hala America/New_York üzerindedir ve istediğiniz saatten altı saat sapmalı olarak çalışır. En çok zaman kaybettiren senaryo budur, çünkü çoğu kişinin ilk yaptığı kontrol artık başarılı sonuç verir.
Bir iş akışı zaman dilimi ayarlayıp daha sonra GENERIC_TIMEZONE değiştirirseniz, iş akışı bu değişikliği yok sayar. İş akışı değeri önceliklidir ve birisi o iş akışının ayarlarını açana kadar bu değer geçerli kalmaya devam eder. Diğerleri düzgün çalışırken garip bir saatte çalışan bir iş akışı, neredeyse her zaman bu durumdan kaynaklanır.
Tahmin etmek yerine saatleri kontrol edin
Ana makine ile container saatini doğrudan karşılaştırın.
date
docker compose exec n8n date
docker compose exec n8n printenv TZ GENERIC_TIMEZONETZ ayarlandığında, ilk iki komut aynı duvar saati zamanını göstermelidir. printenv, mevcut olan her değişken için bir satır yazdırır; dolayısıyla iki satırlık çıktı her ikisinin de ayarlandığı, tek satırlık çıktı ise yarı yapılandırılmış bir durumda olduğunuz anlamına gelir.
Şimdi n8n'e, bir iş akışının içinden kendiniz sorun; çünkü bir container kabuğu size iş akışı düzeyindeki saat diliminin ne olduğunu söyleyemez. Hatalı çalışan iş akışına bir Code düğümü ekleyin ve Execute Workflow ile bir kez çalıştırın.
return [
{
json: {
n8n_time: $now.toISO(),
n8n_zone: $now.zoneName,
system_time: new Date().toString(),
},
},
];n8n_zone, bu iş akışının Schedule Trigger tetikleyicisinin kullanacağı bölgedir ve iş akışı-sonrasında-örnek sırasıyla zaten çözümlenmiştir; bu nedenle soruyu doğrudan yanıtlar. system_time, container'ın kendi bölgesini TZ üzerinden taşır. Bunu yeni bir iş akışında değil, hatalı çalışan iş akışında çalıştırın; çünkü iş akışı düzeyi ayarı, iş akışı ile birlikte hareket eder. Eğer bu iki değer uyuşmuyorsa, tek bir yapılandırma dosyası bile açmadan sorunu bulmuşsunuz demektir.
Schedule Trigger düğümünde Cron ifadeleri
Schedule Trigger, saniyelerden aylara kadar sabit aralıklar sunar; bunların kapsamadığı durumlar için ise Custom (Cron) seçeneği mevcuttur. Cron ifadesi, iş akışının çözümlenmiş saat dilimine göre okunur; bu nedenle 0 6 * * *, UTC 06:00 yerine o saat dilimindeki 06:00 anlamına gelir. crontab guru sitesinden alınan beş alanlı bir ifade olduğu gibi yapıştırılabilir. n8n ayrıca isteğe bağlı bir saniye alanını da kabul eder; dokümantasyonun alan tablosunda bu alan ilk sırada yer alır: saniye, dakika, saat, ayın günü, ay, haftanın günü.
Ofset değerini asla manuel olarak kodlamayın. Berlin'de 06:00 saatine ulaşmak için UTC bir örnek üzerinde 0 4 * * * yazmak kışın doğru, ancak yazın bir saat hatalıdır; çünkü Berlin kışın UTC+1, yazın ise UTC+2 saat dilimini kullanır. Saat dilimini ayarlayın ve kastettiğiniz yerel saati yazın.
Yaz saati uygulamasının 02:30'a ayarlanan bir iş üzerindeki etkisi
Yerel duvar saati zamanı, garantili bir anı temsil etmez. Yılda iki kez bir saat kaybolur ve bir saat tekrarlanır; bu saatler arasına zamanlanan her iş bu durumdan etkilenir. n8n dahil olmaksızın, herhangi bir Linux makinesinde date komutuyla bunun gerçekleştiğini izleyebilirsiniz.
LC_ALL=C TZ=Europe/Berlin date -d '2027-03-28 02:30'date: invalid date '2027-03-28 02:30'Bu komutta bir yazım hatası yoktur. 2027-03-28 tarihinde Berlin saati doğrudan 02:00'den 03:00'e geçer, dolayısıyla o gün 02:30 yerel saati mevcut değildir ve date bunu bir ana dönüştürmeyi reddeder. 02:30 yerel saatine sabitlenmiş bir işin çalışabileceği bir an yoktur. Komşu zamanlar ise sorunsuzdur: date -d '2027-03-28 01:30' CET olarak, date -d '2027-03-28 03:30' ise CEST olarak çözümlenir.
Sonbahar geçişi bunun tam tersidir. 2027-10-31 tarihinde Berlin saati 03:00'ten 02:00'ye geri alınır, bu nedenle 02:30 saati iki kez yaşanır.
LC_ALL=C TZ=Europe/Berlin date -d '2027-10-31 02:30 CEST' '+%s'
LC_ALL=C TZ=Europe/Berlin date -d '2027-10-31 02:30 CET' '+%s'1824942600
1824946200Her ikisi de 02:30 yerel saati olarak adlandırılan ve aralarında 3600 saniye bulunan iki farklı an mevcuttur. Oraya sabitlenmiş bir iş ya iki kez çalışır ya da kimsenin seçmediği bir saatte bir kez çalışır; her iki sonuç da faturalandırma süreci veya yedekleme rotasyonu için istenen durum değildir. Zamanlamayı bu pencerenin dışına taşıyın. Çoğu Avrupa ve Kuzey Amerika bölgesinde riskli aralık 00:00 ile 03:00 yerel saatleri arasındadır.
Altyapıyı UTC zaman diliminde planlayın ve yerel saati kullanıcılara gösterin
Standart yaklaşım, bir zaman diliminin üstlendiği iki görevi birbirinden ayırır. Makineler kararlı bir aralığa ihtiyaç duyar. İnsanlar ise okunabilir bir saate ihtiyaç duyar.
- Kimsenin izlemediği işlemler için iş akışı zaman dilimini UTC olarak ayarlayın. Yedeklemeler, önbellek ısıtma, log aktarımı ve rapor oluşturma bu kategoriye girer. UTC zaman diliminde iki çalışma arasındaki süre, yılın her günü tam olarak yazdığınız süre kadardır; çünkü UTC'de gün ışığından yararlanma uygulaması yoktur.
- Bir insanın okuduğu işler için planlamayı UTC'de tutun ve görüntüleme anında dönüştürün. Tek bir ifade bunu sağlar:
{{ $now.setZone('Europe/Berlin').toFormat('yyyy-MM-dd HH:mm') }}, tetikleyici kararlı kalırken ileti gövdesine yerel saati ekler.
Aynı ayrım n8n dışındaki durumlar için de geçerlidir. Otomasyonunuzun bir parçası bir VPS üzerinde systemd servisi ve zamanlayıcısı olarak çalıştığında, OnCalendar satırı, kendi ayarlarına sahip dördüncü bir saat olan sistem zaman diliminde okunur. Her zamanlayıcıyı UTC'de tutmak, dört kural yerine hatırlamanız gereken tek bir kural bırakır. Bu durum, bir dönemi özetleyen her işlem için de önemlidir; çünkü bir n8n AI agent iş akışı dünün verilerini istediğinde, hangi zaman diliminin çözümlendiğine bağlı olarak sessizce farklı bir 24 saatlik dilimi kullanacaktır.
Hata modları ve karşılaşacağınız çıktılar
Her şey altı saatlik sapmayla çalışıyor. GENERIC_TIMEZONE hiçbir zaman ayarlanmadı, bu nedenle yerleşik varsayılan America/New_York değeri uygulanıyor. docker compose exec n8n printenv GENERIC_TIMEZONE hiçbir çıktı üretmiyor. Bu değeri ayarlayın ve ardından container'ı yeniden oluşturun.
Compose dosyasını düzenlediniz ancak hiçbir şey değişmedi. docker compose restart komutunu çalıştırdınız, bu nedenle container orijinal ortam değişkenlerini korudu. docker compose up -d komutunu çalıştırın ve ardından docker compose exec n8n printenv TZ ile doğrulayın.
Tetikleyici doğru ancak zaman damgaları hatalı. Yalnızca GENERIC_TIMEZONE ayarlanmış durumda. Code düğümü içindeki bir new Date(), işletim sisteminden UTC saatini okumaya devam eder. TZ değerini aynı değere ayarlayın ve yeniden oluşturun.
Bir iş akışı, sunucu ayarını yok sayıyor. Söz konusu iş akışı, kendi ayarlarında tanımlı olan zaman dilimini kullanır ve bu ayar GENERIC_TIMEZONE değerine göre önceliklidir. Çalışma alanını açın, üç noktaya tıklayın, Ayarlar ve ardından Zaman Dilimi yolunu izleyin.
Günlük bir iş bu yıl bir kez iki kez çalıştı veya bir günü atladı. Zamanlanmış saati, gün ışığından yararlanma saati geçişinin içinde kalıyor. Saati değiştirin veya ilgili iş akışını UTC zaman dilimine taşıyın.
FAQ
n8n zamanlayıcı tetikleyicim neden yanlış saatte çalışıyor?
İş akışı, varsaydığınızdan farklı bir saat dilimini çözümlemektedir. n8n, eğer tanımlanmışsa iş akışının saat dilimini, tanımlanmamışsa GENERIC_TIMEZONE üzerinden sunucu saat dilimini, o da yoksa America/New_York varsayılan değerini kullanır. GENERIC_TIMEZONE ayarının yapılmadığı self-hosted bir kurulumda zamanlayıcılar UTC yerine New York saatine göre çalışır; bu nedenle saat farkı, sizin UTC'ye olan uzaklığınızla nadiren eşleşir. docker compose exec n8n printenv GENERIC_TIMEZONE komutunu çalıştırın. Çıktı alınamaması, değerin hiç atanmadığını gösterir.
n8n içindeki TZ ve GENERIC_TIMEZONE arasındaki fark nedir?
TZ, container içindeki işletim sistemi saat dilimidir. Bu ayar, container içinde date komutunun ne döndüreceğini, container günlük kayıtlarındaki zaman damgalarını, bir Code düğümünde new Date() ifadesinin ne sonuç vereceğini ve orada çalıştırdığınız tüm betiklerin ne göreceğini belirler. GENERIC_TIMEZONE ise n8n sunucu saat dilimidir; zamanlayıcı düğümleri ve $now gibi Luxon ifadeleri tarafından kullanılır. Birini ayarlayıp diğerini boş bırakmak, ya doğru tetikleyici ile yanlış zaman damgalarına ya da doğru zaman damgaları ile yanlış saatte çalışan tetikleyicilere yol açar. Her ikisini de aynı değere ayarlayın.
İş akışı saat dilimini mi yoksa GENERIC_TIMEZONE değerini mi ayarlamalıyım?
Tüm sunucu için varsayılan olarak GENERIC_TIMEZONE değerini ayarlayın ve iş akışı bazlı ayarı yalnızca bir iş akışı gerçekten başka bir saat dilimine aitse kullanın. İş akışı değeri, sunucu değerini geçersiz kılar ve GENERIC_TIMEZONE üzerinde yapılan sonraki değişikliklerden etkilenmez; bu nedenle unutulmuş bir iş akışı bazlı geçersiz kılma ayarını aylar sonra tespit etmek zordur.
Saatler değiştiğinde 02:30'a zamanlanmış bir işe ne olur?
Bu yerel saat ya kaybolur ya da iki kez gerçekleşir. Berlin saati o gün 02:00'den 03:00'e atladığı için LC_ALL=C TZ=Europe/Berlin date -d '2027-03-28 02:30', date: invalid date '2027-03-28 02:30' sonucunu döndürür. 2027-10-31 tarihinde ise aynı duvar saati değeri, aralarında bir saat fark olan iki farklı ana karşılık gelir. Zamanlanmış işlerinizi 00:00 ile 03:00 arasındaki yerel saat diliminin dışında tutun veya iş akışını UTC'ye ayarlayıp yerel saate yalnızca bir insan tarafından okunduğu durumlarda dönüştürün.