n8n ile RSS Feed Otomasyonu ve İçerik Dağıtımı
Bir sabah işe geldiğimde Slack’te onlarca mesaj birikmişti. Ekip arkadaşlarım “bu haberi gördün mü?”, “şu blog yazısı çıkmış” diye birbirlerine link atıyordu. Herkes aynı içerikleri manual olarak takip edip paylaşıyordu. O gün öğleden sonra n8n açtım ve bu saçmalığı bitirdim. Bugün size o workflow’u ve üzerine inşa ettiğim sistemi anlatacağım.
n8n RSS Otomasyonuna Neden İhtiyaç Duyarsınız
Sysadmin veya DevOps mühendisi olarak takip etmeniz gereken kaynak sayısı inanılmaz boyutlara ulaşıyor. Kubernetes release notları, CVE duyuruları, cloud provider blog’ları, açık kaynak proje güncellemeleri… Bunları manuel takip etmek hem zaman kaybı hem de gözden kaçırma riskini beraberinde getiriyor.
n8n bu noktada gerçekten parlıyor. Arayüz bazlı çalıştığı için her şeyi kod yazmadan yapabiliyorsunuz, ama ihtiyaç duyduğunuzda JavaScript ile istediğiniz mantığı ekleyebiliyorsunuz. Self-hosted çalıştığınızda verileriniz de tamamen sizde kalıyor, ki kurumsal ortamlarda bu kritik bir faktör.
RSS otomasyonu ise n8n’in en sık kullanılan senaryolarından biri. Basit bir “feed oku, mesaj gönder” akışıyla başlayıp zamanla içerik filtreleme, kategorilendirme, çoklu dağıtım kanalı yönetimine kadar genişletebiliyorsunuz.
Ortamı Hazırlamak
n8n’i Docker ile ayağa kaldırmak en pratik yol. Üretim ortamı için basit bir docker-compose.yml ile başlayalım:
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=n8n.sirketiniz.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://n8n.sirketiniz.com/
- GENERIC_TIMEZONE=Europe/Istanbul
- TZ=Europe/Istanbul
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=guclu_sifreniz
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n_user
- DB_POSTGRESDB_PASSWORD=db_sifreniz
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
postgres:
image: postgres:15
container_name: n8n_postgres
restart: unless-stopped
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n_user
- POSTGRES_PASSWORD=db_sifreniz
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
n8n_data:
postgres_data:
Dikkat edin, timezone’u İstanbul olarak ayarladım. Bu çok önemli, yoksa zamanlanmış işler beklenmedik saatlerde çalışmaya başlıyor ve saçma hata ayıklama süreçlerine giriyorsunuz.
docker compose up -d
docker compose logs -f n8n
İlk başlangıçta n8n admin hesabını oluşturmanızı isteyecek. https://n8n.sirketiniz.com adresine gidin ve kurulumu tamamlayın.
Temel RSS Workflow’u Oluşturmak
n8n arayüzünde “New Workflow” oluşturun. İlk node olarak Schedule Trigger ekleyin. Bu node belirli aralıklarla workflow’u tetikler.
Schedule Trigger ayarları için şunları yapabilirsiniz:
- Her 15 dakikada bir: Önemli güvenlik kaynakları için ideal
- Her saat: Genel teknoloji blogları için yeterli
- Günde bir kez: Haftalık digest’ler için
Ben kritik kaynaklar için 15 dakika, genel içerik için saatlik iki ayrı workflow kuruyorum.
Schedule Trigger’dan sonra RSS Feed Read node’u ekleyin. Bu node n8n’de built-in geliyor ve kullanımı son derece basit.
RSS Feed Read ayarları:
- URL: Takip etmek istediğiniz feed adresi
- Limit: Kaç item çekeceğinizi belirler, genellikle 10-20 yeterli
Birden fazla RSS kaynağını tek workflow’da yönetmek için Merge node’larını kullanabilirsiniz. Ama ben her kaynağı ayrı workflow’da tutmayı tercih ediyorum, sorun çıktığında izole etmek daha kolay oluyor.
Duplicate Kontrolü: En Kritik Adım
RSS otomasyonunun en sık karşılaşılan problemi duplicate içerik. Aynı haber 10 kez Slack’e düşüyorsa herkes sizi blokluyor. Bunu çözmek için birkaç yaklaşım var.
Yaklaşım 1: n8n’in Built-in Deduplication’ı
n8n’de RSS Feed Read node’u son çekilen item’ları hatırlıyor, ama bu sadece aynı workflow içinde çalışıyor ve restart sonrası sıfırlanıyor. Production için yeterli değil.
Yaklaşım 2: Redis ile Kalıcı Deduplication
Docker compose’unuza Redis ekleyin:
redis:
image: redis:7-alpine
container_name: n8n_redis
restart: unless-stopped
volumes:
- redis_data:/data
command: redis-server --appendonly yes
volumes:
redis_data:
Sonra workflow’unuzda Code node ekleyerek şu mantığı uygulayın:
// Code node - Redis ile duplicate kontrolu
const items = $input.all();
const results = [];
for (const item of items) {
const feedItem = item.json;
// Item'in benzersiz ID'sini olustur
const itemId = feedItem.guid || feedItem.link || feedItem.title;
const hashKey = `rss:seen:${Buffer.from(itemId).toString('base64').slice(0, 32)}`;
// Redis'e HTTP request at (n8n HTTP Request node ile de yapilabilir)
// Bu ornekte workflow'da onceden Redis check yaptigimizi varsayiyoruz
results.push({
json: {
...feedItem,
deduplicationKey: hashKey,
processedAt: new Date().toISOString()
}
});
}
return results;
Yaklaşım 3: PostgreSQL ile Geçmiş Tutmak
Benim tercih ettiğim yöntem bu. Çekilen item’ları veritabanına kaydedip her seferinde kontrol ediyorum:
CREATE TABLE IF NOT EXISTS rss_seen_items (
id SERIAL PRIMARY KEY,
item_guid VARCHAR(500) UNIQUE NOT NULL,
feed_source VARCHAR(200),
title TEXT,
published_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_rss_item_guid ON rss_seen_items(item_guid);
CREATE INDEX idx_rss_created_at ON rss_seen_items(created_at);
n8n’de Postgres node ile bu tabloyu kullanabilirsiniz. Önce “SELECT” ile item var mı kontrol edin, yoksa işleyin ve kaydedin.
İçerik Filtreleme ve Kategorilendirme
Ham RSS verisi her zaman kullanılabilir değil. Konu dışı, düşük kaliteli veya alakasız içerikleri filtrelemeniz gerekiyor. Bunun için IF node veya Code node kullanıyorum.
Kubernetes ve container konularıyla ilgilenen bir ekip için örnek filtre:
// Ilgi alani filtresi - Code node
const items = $input.all();
const filtered = [];
const keywords = [
'kubernetes', 'k8s', 'docker', 'container',
'helm', 'istio', 'prometheus', 'grafana',
'devops', 'cicd', 'pipeline', 'terraform',
'ansible', 'linux', 'security', 'cve',
'vulnerability', 'patch', 'update'
];
const negativeKeywords = [
'sponsored', 'advertisement', 'promoted',
'webinar registration', 'sign up now'
];
for (const item of items) {
const text = `${item.json.title} ${item.json.summary || item.json.content || ''}`.toLowerCase();
const hasPositiveMatch = keywords.some(kw => text.includes(kw));
const hasNegativeMatch = negativeKeywords.some(kw => text.includes(kw));
if (hasPositiveMatch && !hasNegativeMatch) {
// Kategori belirle
let category = 'Genel';
if (text.includes('cve') || text.includes('vulnerability') || text.includes('security')) {
category = 'Guvenlik';
} else if (text.includes('kubernetes') || text.includes('k8s')) {
category = 'Kubernetes';
} else if (text.includes('terraform') || text.includes('ansible')) {
category = 'IaC';
}
filtered.push({
json: {
...item.json,
category: category,
relevanceScore: keywords.filter(kw => text.includes(kw)).length
}
});
}
}
// Relevance score'a gore sirala
filtered.sort((a, b) => b.json.relevanceScore - a.json.relevanceScore);
return filtered;
Slack’e Dağıtım
Filtrelenen içerikleri Slack’e göndermek için Slack node’unu kullanıyorum. Basit metin mesajı yerine Block Kit ile formatlı mesaj göndermek çok daha etkili.
// Slack mesaj formati - Code node ile olustur
const item = $input.first().json;
const emoji = {
'Guvenlik': ':rotating_light:',
'Kubernetes': ':wheel_of_dharma:',
'IaC': ':hammer_and_wrench:',
'Genel': ':newspaper:'
};
const categoryEmoji = emoji[item.category] || ':newspaper:';
return [{
json: {
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `${categoryEmoji} *[${item.category}]* ${item.title}`
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: item.summary
? item.summary.replace(/<[^>]*>/g, '').slice(0, 300) + '...'
: 'Ozet mevcut degil'
}
},
{
type: "actions",
elements: [
{
type: "button",
text: {
type: "plain_text",
text: "Makaleyi Oku"
},
url: item.link,
style: "primary"
}
]
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text: `Kaynak: ${item.feedSource || 'Bilinmiyor'} | ${new Date(item.pubDate).toLocaleString('tr-TR', {timeZone: 'Europe/Istanbul'})}`
}
]
}
]
}
}];
Farklı kategoriler için farklı Slack kanallarına göndermek de mümkün. IF node ile category değerine göre farklı Slack node’larına yönlendirin.
E-posta Digest Sistemi
Anlık Slack bildirimleri dışında haftalık özet e-posta göndermek de çok işe yarıyor. Bunun için ayrı bir workflow kuruyorum.
// Haftalik digest icin PostgreSQL'den son 7 gunun verilerini cek
// SQL Query node'unda kullan:
SELECT
category,
title,
link,
feed_source,
published_at
FROM rss_seen_items
WHERE created_at >= NOW() - INTERVAL '7 days'
ORDER BY category, published_at DESC;
Bu sorgu sonucunu HTML e-postaya dönüştürmek için:
// E-posta HTML olusturucu - Code node
const items = $input.all();
// Kategorilere gore grupla
const grouped = {};
for (const item of items) {
const cat = item.json.category || 'Genel';
if (!grouped[cat]) grouped[cat] = [];
grouped[cat].push(item.json);
}
let htmlContent = `
<html>
<body style="font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto;">
<h1 style="color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px;">
Haftalik Tech Digest - ${new Date().toLocaleDateString('tr-TR')}
</h1>
`;
for (const [category, categoryItems] of Object.entries(grouped)) {
htmlContent += `
<h2 style="color: #2980b9; margin-top: 30px;">${category}</h2>
<ul style="list-style: none; padding: 0;">
`;
for (const article of categoryItems.slice(0, 5)) {
htmlContent += `
<li style="margin-bottom: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px;">
<a href="${article.link}" style="color: #2c3e50; text-decoration: none; font-weight: bold;">
${article.title}
</a>
<div style="color: #7f8c8d; font-size: 12px; margin-top: 5px;">
${article.feed_source} - ${new Date(article.published_at).toLocaleDateString('tr-TR')}
</div>
</li>
`;
}
htmlContent += '</ul>';
}
htmlContent += `
<p style="color: #95a5a6; font-size: 12px; margin-top: 40px; border-top: 1px solid #ecf0f1; padding-top: 10px;">
Bu e-posta n8n RSS Otomasyonu tarafindan otomatik olarak olusturulmustur.
</p>
</body></html>`;
return [{ json: { html: htmlContent, subject: `Haftalik Tech Digest - ${new Date().toLocaleDateString('tr-TR')}` } }];
Telegram Bot Entegrasyonu
Bazı ekipler Slack yerine Telegram kullanıyor. n8n’de Telegram node built-in geliyor ve kurulumu çok basit.
Önce BotFather ile bir bot oluşturun, token’ı alın. n8n’de Telegram credential’ı ekleyin. Sonra workflow’unuza Telegram node ekleyip şu şekilde kullanın:
// Telegram mesaj formati
const item = $input.first().json;
const message = `
*${item.category}* | ${item.title}
${item.summary ? item.summary.replace(/<[^>]*>/g, '').slice(0, 200) + '...' : ''}
[Makaleyi Oku](${item.link})
_${item.feedSource} | ${new Date(item.pubDate).toLocaleString('tr-TR', {timeZone: 'Europe/Istanbul'})}_
`;
return [{ json: { text: message } }];
Telegram node ayarları:
- Operation: Send Message
- Chat ID: Kanal veya grup ID’niz (kanal için @kanaladi, grup için negatif sayı)
- Text: Yukarıdaki JavaScript’ten gelen text değeri
- Parse Mode: MarkdownV2
Hata Yönetimi ve İzleme
Production’da hatasız çalışan workflow yok. n8n’de hata yönetimi için birkaç pratik yaklaşım var.
Her workflow için Error Workflow tanımlayın. n8n Settings > Workflows bölümünden default error workflow ayarlayabilirsiniz.
// Error workflow - Code node ile hata bildirim mesaji olustur
const error = $input.first().json;
return [{
json: {
errorMessage: error.message || 'Bilinmeyen hata',
workflowName: error.workflow?.name || 'Bilinmiyor',
nodeName: error.node?.name || 'Bilinmiyor',
timestamp: new Date().toISOString(),
slackMessage: `:x: *n8n Workflow Hatasi*n*Workflow:* ${error.workflow?.name}n*Node:* ${error.node?.name}n*Hata:* ${error.message}n*Zaman:* ${new Date().toLocaleString('tr-TR', {timeZone: 'Europe/Istanbul'})}`
}
}];
Execution geçmişini de izleyin. n8n’de her execution kayıt altına alınıyor, ama bu veritabanını şişirebilir. PostgreSQL kullanıyorsanız eski execution’ları temizlemek için:
# n8n veritabaninda eski execution'lari temizle
# Bu komutu cron ile aylik calistirabilirsiniz
docker exec -it n8n_postgres psql -U n8n_user -d n8n -c "
DELETE FROM execution_entity
WHERE stopped_at < NOW() - INTERVAL '30 days'
AND status IN ('success', 'error');
"
Gerçek Dünya Senaryo: CVE Takip Sistemi
Güvenlik ekipleri için kurduğum en değerli workflow CVE takip sistemi. NIST NVD, Exploit-DB ve vendor security advisory feed’lerini birleştirip kritik CVE’leri anında bildiriyor.
// CVE kritiklik kontrolu - Code node
const items = $input.all();
const critical = [];
for (const item of items) {
const text = `${item.json.title} ${item.json.summary || ''}`.toLowerCase();
// CVSS score kontrolu
const cvssMatch = text.match(/cvss[:s]+(d+.?d*)/i);
const cvssScore = cvssMatch ? parseFloat(cvssMatch[1]) : 0;
// Kritik sistemler
const criticalSystems = ['linux kernel', 'openssl', 'apache', 'nginx',
'kubernetes', 'docker', 'vmware', 'cisco', 'fortigate'];
const affectsCritical = criticalSystems.some(sys => text.includes(sys));
// Exploit mevcut mu?
const hasExploit = text.includes('exploit') || text.includes('poc') ||
text.includes('proof of concept') || text.includes('actively exploited');
let priority = 'normal';
if (cvssScore >= 9.0 || (cvssScore >= 7.0 && hasExploit)) {
priority = 'critical';
} else if (cvssScore >= 7.0 || (affectsCritical && hasExploit)) {
priority = 'high';
}
if (priority !== 'normal') {
critical.push({
json: {
...item.json,
cvssScore,
hasExploit,
affectsCritical,
priority
}
});
}
}
return critical;
Critical CVE’ler için PagerDuty veya OpsGenie entegrasyonu da ekleyebilirsiniz. n8n’de her ikisi için de built-in node var.
Workflow Performansı ve Optimizasyon
Çok sayıda RSS feed takip ettiğinizde n8n performansı önemli hale geliyor. Birkaç optimizasyon önerisi:
Workflow’larınızı paralel çalıştırmak yerine sıralı çalıştırın, özellikle aynı veritabanına yazıyorlarsa. n8n’de Wait node kullanarak workflow’lar arası gecikme ekleyebilirsiniz.
N8N_EXECUTIONS_PROCESS environment variable’ını main olarak bırakın küçük kurulumlar için. Büyük kurulumlar için queue moduna geçin ve bir worker ekleyin:
# Queue mode icin ek environment degiskenleri
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- N8N_CONCURRENCY_PRODUCTION_LIMIT=10
Sonuç
n8n ile RSS otomasyonu başlangıçta basit görünen ama üzerine çok şey inşa edebileceğiniz bir sistem. Temel akış çok basit: feed oku, filtrele, dağıt. Ama duplicate kontrolü, kategorilendirme, çoklu kanal yönetimi, hata işleme ve CVE gibi özel senaryolar eklendikçe gerçekten güçlü bir içerik istihbarat sistemi haline geliyor.
Benim tavsiyem küçük başlamak. Tek bir RSS feed, tek bir Slack kanalı ile başlayın. Çalışır hale getirin, sonra genişletin. Her seferinde bir şey ekleyin. İki ay içinde farkında olmadan ekibinizin vazgeçemediği bir sistemi kurmuş olursunuz.
Self-hosted çalıştırdığınız için verileriniz sizde, maliyetiniz minimal, özelleştirme imkanınız sınırsız. Kurumsal ortamlarda bu kombinasyon çok değerli. n8n community’si de oldukça aktif, karşılaştığınız sorunların büyük çoğunluğu için forum’da hazır çözüm bulabiliyorsunuz.
Workflow’larınızı düzenli olarak export edip Git’e commit etmeyi de unutmayın. Bir gün işinizi kurtarır.
