This workflow corresponds to n8n.io template #9613 — we link there as the canonical source.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "cM2tIagg9TD8Jc0l",
"name": "Kubernetes Monitoring & Alert - Deplyoment & Pods Status - Telegram Alert",
"tags": [],
"nodes": [
{
"id": "f0b0f7f0-b5e1-4bfd-85a0-6c0f90336a6e",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-480,
208
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes"
}
]
}
},
"typeVersion": 1
},
{
"id": "976ee100-553e-4b40-8b9f-09a3afd980a9",
"name": "Kubeconfig Setup",
"type": "n8n-nodes-base.code",
"position": [
-224,
208
],
"parameters": {
"jsCode": "// PASTE YOUR KUBECONFIG CONTENT HERE\nconst kubeconfigContent = `\napiVersion: v1\nkind: Config\nclusters:\n- cluster:\n certificate-authority-data: YOUR_CA_DATA\n server: https://your-k8s-api-server:6443\n name: your-cluster\ncontexts:\n- context:\n cluster: your-cluster\n user: your-user\n name: your-context\ncurrent-context: your-context\nusers:\n- name: your-user\n user:\n token: YOUR_TOKEN\n`;\n\n// Configuration\nconst namespace = 'production';\n\nreturn [{\n json: {\n kubeconfig: kubeconfigContent,\n namespace: namespace\n }\n}];"
},
"typeVersion": 2
},
{
"id": "b95b994d-00b2-4bb7-9d4b-9f417f8bd1e1",
"name": "Get Pods",
"type": "n8n-nodes-base.executeCommand",
"position": [
0,
0
],
"parameters": {
"command": "=apk add curl\nKUBECONFIG_PATH=\"/tmp/kubeconfig-$RANDOM.yaml\"\necho '{{$json.kubeconfig}}' > $KUBECONFIG_PATH\ncurl -LO https://dl.k8s.io/release/v1.34.0/bin/linux/amd64/kubectl\nchmod +x ./kubectl\n./kubectl --kubeconfig=$KUBECONFIG_PATH get pods -n {{$json.namespace}} -o json\nrm -f $KUBECONFIG_PATH"
},
"typeVersion": 1
},
{
"id": "0b5b3388-2de0-479e-a9d3-a1b136b0cf56",
"name": "Get Deployments",
"type": "n8n-nodes-base.executeCommand",
"position": [
-16,
432
],
"parameters": {
"command": "=apk add curl\nKUBECONFIG_PATH=\"/tmp/kubeconfig-$RANDOM.yaml\"\necho '{{$json.kubeconfig}}' > $KUBECONFIG_PATH\ncurl -LO https://dl.k8s.io/release/v1.34.0/bin/linux/amd64/kubectl\nchmod +x ./kubectl\n./kubectl --kubeconfig=$KUBECONFIG_PATH get deployments -n {{$json.namespace}} -o json\nrm -f $KUBECONFIG_PATH"
},
"typeVersion": 1
},
{
"id": "f0a55a90-5bcf-42dc-96b0-3a21bd83c12e",
"name": "Process & Generate Report",
"type": "n8n-nodes-base.code",
"position": [
208,
224
],
"parameters": {
"jsCode": "// Debug: Check inputs\nconst inputs = $input.all();\n\n// Try to identify which input is which\nlet podsData, deploymentsData;\n\nfor (let i = 0; i < inputs.length; i++) {\n try {\n const input = inputs[i];\n \n if (!input.json?.stdout) continue;\n \n const data = JSON.parse(input.json.stdout);\n \n if (data.items && data.items.length > 0) {\n const firstKind = data.items[0].kind;\n \n if (firstKind === 'Pod') {\n podsData = data;\n } else if (firstKind === 'Deployment') {\n deploymentsData = data;\n }\n }\n } catch (e) {\n console.log(`Error parsing input ${i}:`, e.message);\n }\n}\n\nconst pods = podsData?.items || [];\nconst deployments = deploymentsData?.items || [];\nconst namespace = podsData?.items?.[0]?.metadata?.namespace || deploymentsData?.items?.[0]?.metadata?.namespace || 'N/A';\n\nconst deploymentStatus = {};\nconst otherWorkloads = {};\nconst standalonePods = [];\nconst alerts = [];\n\n// Process deployments from deployment objects\ndeployments.forEach(deployment => {\n const name = deployment.metadata.name;\n const ns = deployment.metadata.namespace;\n const replicas = deployment.spec.replicas || 0;\n const readyReplicas = deployment.status.readyReplicas || 0;\n const availableReplicas = deployment.status.availableReplicas || 0;\n \n deploymentStatus[name] = {\n name: name,\n namespace: ns,\n replicas: replicas,\n readyReplicas: readyReplicas,\n availableReplicas: availableReplicas,\n pods: []\n };\n \n if (readyReplicas < 1) {\n alerts.push({\n workload: name,\n kind: 'Deployment',\n issue: `No ready pods available! (Ready: ${readyReplicas}/${replicas})`\n });\n }\n});\n\n// Process pods - group by owner\npods.forEach(pod => {\n const podName = pod.metadata.name;\n const phase = pod.status.phase;\n const nodeName = pod.spec.nodeName || 'N/A';\n const conditions = pod.status.conditions || [];\n const readyCondition = conditions.find(c => c.type === 'Ready');\n const isReady = readyCondition?.status === 'True';\n const restartCount = pod.status.containerStatuses?.reduce((sum, c) => sum + c.restartCount, 0) || 0;\n \n const podInfo = {\n name: podName,\n phase: phase,\n ready: isReady,\n node: nodeName,\n restarts: restartCount\n };\n \n // Find owner from ownerReferences\n const ownerRefs = pod.metadata.ownerReferences || [];\n let ownerName = null;\n let ownerKind = null;\n \n for (const owner of ownerRefs) {\n if (owner.kind === 'ReplicaSet') {\n // Extract deployment name from ReplicaSet name\n const parts = owner.name.split('-');\n if (parts.length > 1) {\n parts.pop();\n ownerName = parts.join('-');\n ownerKind = 'Deployment';\n }\n break;\n } else if (['DaemonSet', 'StatefulSet', 'Node'].includes(owner.kind)) {\n ownerName = owner.name;\n ownerKind = owner.kind;\n break;\n }\n }\n \n // Add pod to appropriate owner\n if (ownerKind === 'Deployment') {\n // If deployment exists in deploymentStatus, add there\n if (deploymentStatus[ownerName]) {\n deploymentStatus[ownerName].pods.push(podInfo);\n } else {\n // Otherwise create a deployment entry (discovered from pods)\n if (!deploymentStatus[ownerName]) {\n const readyCount = 0; // Will be calculated later\n deploymentStatus[ownerName] = {\n name: ownerName,\n namespace: namespace,\n replicas: 0,\n readyReplicas: 0,\n availableReplicas: 0,\n pods: [],\n discoveredFromPods: true\n };\n }\n deploymentStatus[ownerName].pods.push(podInfo);\n }\n } else if (ownerName) {\n // Group by other owner types\n const key = `${ownerKind}/${ownerName}`;\n if (!otherWorkloads[key]) {\n otherWorkloads[key] = {\n kind: ownerKind,\n name: ownerName,\n pods: []\n };\n }\n otherWorkloads[key].pods.push(podInfo);\n } else {\n standalonePods.push(podInfo);\n }\n});\n\n// Calculate stats for deployments discovered from pods\nObject.values(deploymentStatus).forEach(dep => {\n if (dep.discoveredFromPods) {\n const totalPods = dep.pods.length;\n const readyPods = dep.pods.filter(p => p.ready).length;\n dep.replicas = totalPods;\n dep.readyReplicas = readyPods;\n dep.availableReplicas = readyPods;\n \n if (readyPods < 1) {\n alerts.push({\n workload: dep.name,\n kind: 'Deployment',\n issue: `No ready pods available! (Ready: ${readyPods}/${totalPods})`\n });\n }\n }\n});\n\n// Check for alerts in non-deployment workloads\nObject.values(otherWorkloads).forEach(owner => {\n const readyCount = owner.pods.filter(p => p.ready).length;\n if (readyCount < 1) {\n alerts.push({\n workload: owner.name,\n kind: owner.kind,\n issue: `No ready pods available! (Ready: ${readyCount}/${owner.pods.length})`\n });\n }\n});\n\n// Generate report\nlet markdown = `# Kubernetes Cluster Status Report\\n\\n`;\nmarkdown += `**Namespace:** ${namespace}\\n`;\nmarkdown += `**Timestamp:** ${new Date().toISOString()}\\n\\n`;\n\nconst deploymentCount = Object.keys(deploymentStatus).length;\nconst otherWorkloadCount = Object.keys(otherWorkloads).length;\nmarkdown += `**Total:** ${deploymentCount} deployments, ${otherWorkloadCount} other workloads, ${pods.length} pods\\n\\n`;\n\n// Deployments section\nif (deploymentCount > 0) {\n markdown += `## Deployments\\n\\n`;\n Object.values(deploymentStatus).forEach(dep => {\n const status = dep.readyReplicas >= 1 ? '\u2705' : '\u274c';\n markdown += `### ${status} ${dep.name}\\n`;\n markdown += `- **Replicas:** ${dep.readyReplicas}/${dep.replicas}\\n`;\n markdown += `- **Available:** ${dep.availableReplicas}\\n`;\n markdown += `- **Pods:**\\n`;\n \n if (dep.pods.length === 0) {\n markdown += ` - *No pods*\\n`;\n } else {\n dep.pods.forEach(pod => {\n const podStatus = pod.ready ? '\u2705' : '\u274c';\n markdown += ` - ${podStatus} ${pod.name} (${pod.phase}) - Node: ${pod.node}, Restarts: ${pod.restarts}\\n`;\n });\n }\n markdown += `\\n`;\n });\n}\n\n// Other workloads section (DaemonSets, StatefulSets, Static Pods)\nif (otherWorkloadCount > 0) {\n markdown += `## Other Workloads (DaemonSets, StatefulSets, Static Pods)\\n\\n`;\n Object.values(otherWorkloads).forEach(owner => {\n const readyCount = owner.pods.filter(p => p.ready).length;\n const totalCount = owner.pods.length;\n const status = readyCount >= 1 ? '\u2705' : '\u274c';\n \n markdown += `### ${status} ${owner.name} (${owner.kind})\\n`;\n markdown += `- **Ready:** ${readyCount}/${totalCount}\\n`;\n markdown += `- **Pods:**\\n`;\n \n owner.pods.forEach(pod => {\n const podStatus = pod.ready ? '\u2705' : '\u274c';\n markdown += ` - ${podStatus} ${pod.name} (${pod.phase}) - Node: ${pod.node}, Restarts: ${pod.restarts}\\n`;\n });\n markdown += `\\n`;\n });\n}\n\n// Standalone pods\nif (standalonePods.length > 0) {\n markdown += `## Standalone Pods\\n\\n`;\n standalonePods.forEach(pod => {\n const podStatus = pod.ready ? '\u2705' : '\u274c';\n markdown += `- ${podStatus} ${pod.name} (${pod.phase}) - Node: ${pod.node}, Restarts: ${pod.restarts}\\n`;\n });\n markdown += `\\n`;\n}\n\n// Alerts section\nif (alerts.length > 0) {\n markdown += `## \u26a0\ufe0f Alerts\\n\\n`;\n alerts.forEach(alert => {\n markdown += `- **${alert.workload}** (${alert.kind}): ${alert.issue}\\n`;\n });\n}\n\n// Convert markdown to binary data\nconst buffer = Buffer.from(markdown, 'utf-8');\nconst binaryData = {\n data: buffer.toString('base64'),\n mimeType: 'text/markdown',\n fileName: 'report.md'\n};\n\nreturn [{\n json: {\n markdown: markdown,\n hasAlerts: alerts.length > 0,\n alerts: alerts,\n deploymentCount: deploymentCount,\n podCount: pods.length,\n namespace: namespace\n },\n binary: {\n data: binaryData\n }\n}];"
},
"typeVersion": 2
},
{
"id": "22af800d-d130-4d13-9e6b-6a9176b886b9",
"name": "Has Alerts?",
"type": "n8n-nodes-base.if",
"position": [
432,
224
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{$json.hasAlerts}}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"id": "deb746c1-42b4-4e12-8530-d0340cf52e90",
"name": "Send Telegram Alert",
"type": "n8n-nodes-base.telegram",
"position": [
704,
16
],
"parameters": {
"text": "=\ud83d\udea8 *Kubernetes Alert*\\n\\n*Namespace:* {{$json.namespace}}\\n\\n{{$json.alerts.map(a => `\u26a0\ufe0f *${a.deployment}*: ${a.issue}`).join('\\n')}}\\n\\n---\\n\\n{{$json.markdown}}",
"chatId": "YOUR_TELEGRAM_CHAT_ID",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "c8cf22c0-7954-4a5c-84fc-3513ece1e376",
"name": "Save Report",
"type": "n8n-nodes-base.writeBinaryFile",
"position": [
704,
480
],
"parameters": {
"options": {},
"fileName": "=k8s-report-{{$now.format('YYYY-MM-DD-HHmmss')}}.md",
"dataPropertyName": "=data"
},
"typeVersion": 1
},
{
"id": "277ca9f2-b6bf-4c87-8d56-1734c0fecae0",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
0
],
"parameters": {
"width": 360,
"height": 180,
"content": "## Kubernetes Monitoring Workflow\n\nAutomatically monitors your K8s cluster and sends Telegram alerts when workloads have zero ready pods.\n\nReports are saved as markdown files for every execution."
},
"typeVersion": 1
},
{
"id": "da5b3125-2dfd-4ee5-9732-27404ef98455",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-416,
352
],
"parameters": {
"width": 300,
"height": 240,
"content": "## CONFIGURATION REQUIRED\n\n1. Paste your kubeconfig content\n2. Set target namespace (default: 'production')\n\nThe workflow will automatically download kubectl during execution."
},
"typeVersion": 1
},
{
"id": "848a775e-d3c7-4c5b-8b1b-6ad4200b9687",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-96,
-208
],
"parameters": {
"width": 280,
"height": 188,
"content": "## Data Collection\n\nBoth nodes run in parallel to fetch:\n- All pods\n- All deployments\n\nfrom the specified namespace"
},
"typeVersion": 1
},
{
"id": "5e94eb86-fb7a-4cde-a2b5-3778f057c316",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
384
],
"parameters": {
"width": 280,
"content": "## Processing & Alert Detection\n\nGroups pods by owner (Deployment, DaemonSet, StatefulSet, Node)\n\nDetects alerts: workloads with 0 ready pods\n\nGenerates comprehensive markdown report"
},
"typeVersion": 1
},
{
"id": "83b169f1-7bf5-497d-bdb8-9b999baaa980",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
704,
-224
],
"parameters": {
"width": 464,
"height": 216,
"content": "## TELEGRAM CONFIGURATION\n\n1. Create bot via @BotFather\n2. Get bot token & add as credential\n3. Replace YOUR_TELEGRAM_CHAT_ID with your actual chat ID\n\nFind chat ID: message your bot, then visit:\nhttps://api.telegram.org/bot<TOKEN>/getUpdates"
},
"typeVersion": 1
},
{
"id": "00a9cb9f-6e0b-4537-adee-b2b6cef02f86",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
704,
624
],
"parameters": {
"width": 280,
"height": 220,
"content": "## Report Output\n\nSaves markdown report with timestamp:\nk8s-report-YYYY-MM-DD-HHmmss.md\n\nExecutes on every run regardless of alert status"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "0f3068a8-25a9-42af-b277-7deb8c6844f1",
"connections": {
"Get Pods": {
"main": [
[
{
"node": "Process & Generate Report",
"type": "main",
"index": 0
}
]
]
},
"Has Alerts?": {
"main": [
[
{
"node": "Send Telegram Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Save Report",
"type": "main",
"index": 0
}
]
]
},
"Get Deployments": {
"main": [
[
{
"node": "Process & Generate Report",
"type": "main",
"index": 0
}
]
]
},
"Kubeconfig Setup": {
"main": [
[
{
"node": "Get Pods",
"type": "main",
"index": 0
},
{
"node": "Get Deployments",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Kubeconfig Setup",
"type": "main",
"index": 0
}
]
]
},
"Send Telegram Alert": {
"main": [
[
{
"node": "Save Report",
"type": "main",
"index": 0
}
]
]
},
"Process & Generate Report": {
"main": [
[
{
"node": "Has Alerts?",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
telegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Open the "Kubeconfig Setup" node Paste your entire kubeconfig file content into the variable Set your target namespace in the variable (default: 'production')
Source: https://n8n.io/workflows/9613/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
⚠️ Heads up: this is satire. The "Hell Yeah!" workflow is a parody of "automate your whole life with AI agents" grindset content. The API endpoints are fictional and the function nodes are illustrativ
This n8n workflow provides automated monitoring of Public Key Infrastructure (PKI) components including CA certificates, Certificate Revocation Lists (CRLs), and associated web services. It extracts c
This workflow will monitor the price of a token trading pair (default is ETH - USDC) and automatically buy into ETH or sell into USDC based on a price window configured by the user.
This workflow continuously monitors the Meta Ads Library for new creatives from a specific competitor pages, logs them into Google Sheets, and sends a concise Telegram notification with the number of
This n8n workflow template is designed to provide real-time alerts on new cryptocurrency exchange listings and delistings. It caters especially to crypto traders, investors, and enthusiasts who want t