Self-hosting and Client Delivery
Module 12 · ship · advanced
learner.json
In n8n, copy this file and paste it onto the canvas, or use Import from File.
{
"name": "12 - Uptime Monitor",
"nodes": [
{
"parameters": {
"rule": { "interval": [{ "field": "minutes", "minutesInterval": 5 }] }
},
"id": "m12-schedule",
"name": "Every 5 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [0, 0]
},
{
"parameters": {
"path": "ops/health",
"responseMode": "responseNode",
"options": {}
},
"id": "m12-webhook",
"name": "Health API",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [0, 200],
"webhookId": "course-12-health"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "u1",
"name": "urls",
"value": "={{ $json.query?.urls ? $json.query.urls.split(',').map(u => u.trim()).filter(Boolean) : ['http://localhost:5678/healthz', 'https://example.com'] }}",
"type": "array"
}
]
},
"options": {}
},
"id": "m12-settings",
"name": "Sites to Check (edit me)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [220, 100]
},
{
"parameters": { "fieldToSplitOut": "urls", "options": { "destinationFieldName": "url" } },
"id": "m12-split",
"name": "One Item per Site",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [440, 100]
},
{
"parameters": {
"url": "={{ $json.url }}",
"options": {
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "text" } },
"timeout": 8000,
"redirect": { "redirect": { "maxRedirects": 3 } }
}
},
"id": "m12-check",
"name": "Check Site",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [660, 100],
"onError": "continueRegularOutput"
},
{
"parameters": {
"assignments": {
"assignments": [
{ "id": "e1", "name": "url", "value": "={{ $('One Item per Site').item.json.url }}", "type": "string" },
{ "id": "e2", "name": "status", "value": "={{ $json.statusCode ?? 0 }}", "type": "number" },
{ "id": "e3", "name": "ok", "value": "={{ ($json.statusCode ?? 0) >= 200 && ($json.statusCode ?? 0) < 400 }}", "type": "boolean" },
{ "id": "e4", "name": "error", "value": "={{ $json.error?.message ?? ($json.statusCode >= 400 ? 'HTTP ' + $json.statusCode : '') }}", "type": "string" },
{ "id": "e5", "name": "checked_at", "value": "={{ $now.toISO() }}", "type": "string" }
]
},
"options": {}
},
"id": "m12-evaluate",
"name": "Evaluate",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [880, 100]
},
{
"parameters": { "aggregate": "aggregateAllItemData", "destinationFieldName": "checks", "options": {} },
"id": "m12-agg",
"name": "All Checks",
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [1100, 100]
},
{
"parameters": {
"assignments": {
"assignments": [
{ "id": "s1", "name": "all_ok", "value": "={{ $json.checks.every(c => c.ok) }}", "type": "boolean" },
{ "id": "s2", "name": "down", "value": "={{ $json.checks.filter(c => !c.ok).map(c => c.url) }}", "type": "array" },
{ "id": "s3", "name": "checks", "value": "={{ $json.checks }}", "type": "array" },
{ "id": "s4", "name": "alert", "value": "={{ '🔴 DOWN: ' + $json.checks.filter(c => !c.ok).map(c => c.url + ' (' + (c.error || 'status ' + c.status) + ')').join(', ') }}", "type": "string" }
]
},
"options": {}
},
"id": "m12-summary",
"name": "Summary",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [1320, 100]
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
"conditions": [
{
"id": "a1",
"leftValue": "={{ $('Health API').isExecuted }}",
"rightValue": "",
"operator": { "type": "boolean", "operation": "true", "singleValue": true }
}
],
"combinator": "and"
},
"options": {}
},
"id": "m12-api",
"name": "Called via API?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [1540, 100]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { all_ok: $json.all_ok, down: $json.down, checks: $json.checks } }}",
"options": { "responseCode": "={{ $json.all_ok ? 200 : 503 }}" }
},
"id": "m12-respond",
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [1760, 0]
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
"conditions": [
{
"id": "d1",
"leftValue": "={{ $json.all_ok }}",
"rightValue": "",
"operator": { "type": "boolean", "operation": "false", "singleValue": true }
}
],
"combinator": "and"
},
"options": {}
},
"id": "m12-down",
"name": "Anything Down?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [1760, 200]
},
{
"parameters": {},
"id": "m12-alert",
"name": "Send Alert (Exercise 1)",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1980, 120]
}
],
"connections": {
"Every 5 Minutes": { "main": [[{ "node": "Sites to Check (edit me)", "type": "main", "index": 0 }]] },
"Health API": { "main": [[{ "node": "Sites to Check (edit me)", "type": "main", "index": 0 }]] },
"Sites to Check (edit me)": { "main": [[{ "node": "One Item per Site", "type": "main", "index": 0 }]] },
"One Item per Site": { "main": [[{ "node": "Check Site", "type": "main", "index": 0 }]] },
"Check Site": { "main": [[{ "node": "Evaluate", "type": "main", "index": 0 }]] },
"Evaluate": { "main": [[{ "node": "All Checks", "type": "main", "index": 0 }]] },
"All Checks": { "main": [[{ "node": "Summary", "type": "main", "index": 0 }]] },
"Summary": { "main": [[{ "node": "Called via API?", "type": "main", "index": 0 }]] },
"Called via API?": {
"main": [
[{ "node": "Respond", "type": "main", "index": 0 }],
[{ "node": "Anything Down?", "type": "main", "index": 0 }]
]
},
"Anything Down?": { "main": [[{ "node": "Send Alert (Exercise 1)", "type": "main", "index": 0 }]] }
},
"pinData": {},
"settings": { "executionOrder": "v1" }
}