{
  "name": "Fleet Health Check (free, by Aviary)",
  "nodes": [
    {
      "parameters": {
        "content": "## Fleet Health Check — free template by Aviary\n\nPings every n8n instance in your fleet on a schedule and alerts Slack when one stops answering.\n\n**Setup (2 minutes):**\n1. Edit the **Instances** node — one line per instance (name + base URL).\n2. Each instance must expose `/healthz` — set `QUEUE_HEALTH_CHECK_ACTIVE=true` / ensure the endpoint is reachable from this instance.\n3. Pick your Slack credential + channel in the **Alert Slack** node (or swap it for Email/Telegram).\n4. Activate the workflow.\n\n**Known limit (by design):** this runs *inside* one of your n8n instances — if **that** host dies, the watcher dies with it, and it can't verify your backups actually restore. That gap is what we're building Aviary for → https://aviary.featherapps.online",
        "height": 420,
        "width": 460
      },
      "id": "note-1",
      "name": "Read me",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-220, -180]
    },
    {
      "parameters": {
        "rule": {
          "interval": [{ "field": "minutes", "minutesInterval": 5 }]
        }
      },
      "id": "trigger-1",
      "name": "Every 5 minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [280, 0]
    },
    {
      "parameters": {
        "jsCode": "// EDIT ME — one entry per n8n instance you run.\nconst instances = [\n  { name: 'client-a', url: 'https://n8n.client-a.example.com' },\n  { name: 'client-b', url: 'https://n8n.client-b.example.com' },\n];\nreturn instances.map(i => ({ json: i }));"
      },
      "id": "code-1",
      "name": "Instances",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [500, 0]
    },
    {
      "parameters": {
        "url": "={{ $json.url.replace(/\\/+$/, '') }}/healthz",
        "options": { "timeout": 10000 }
      },
      "id": "http-1",
      "name": "Check /healthz",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [720, 0],
      "onError": "continueRegularOutput",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// Pair results back to instances by index; emit ONE item PER FAILED instance.\n// Empty output = all healthy = nothing downstream runs.\nconst instances = $('Instances').all();\nconst results = $input.all();\nconst failed = [];\nfor (let i = 0; i < instances.length; i++) {\n  const inst = instances[i].json;\n  const res = results[i]?.json ?? {};\n  const ok = res.status === 'ok' && res.error === undefined;\n  if (!ok) {\n    failed.push({ json: {\n      name: inst.name,\n      url: inst.url,\n      reason: res.error ? String(res.error).slice(0, 200) : `unexpected response: ${JSON.stringify(res).slice(0, 200)}`,\n      checkedAt: new Date().toISOString(),\n    }});\n  }\n}\nreturn failed;"
      },
      "id": "code-2",
      "name": "Keep only failures",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [940, 0]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": { "__rl": true, "mode": "name", "value": "#alerts" },
        "text": "=🚨 n8n instance not responding: *{{ $json.name }}*\n{{ $json.url }}\nreason: {{ $json.reason }}\nchecked: {{ $json.checkedAt }}",
        "otherOptions": {}
      },
      "id": "slack-1",
      "name": "Alert Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [1160, 0]
    },
    {
      "parameters": {
        "content": "**Swap the alert:** don't use Slack? Replace this node with *Send Email*, *Telegram*, or an HTTP call to your paging tool — the failure items carry `name`, `url`, `reason`, `checkedAt`.",
        "height": 140,
        "width": 300
      },
      "id": "note-2",
      "name": "Alert options",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [1120, -200]
    }
  ],
  "connections": {
    "Every 5 minutes": { "main": [[{ "node": "Instances", "type": "main", "index": 0 }]] },
    "Instances": { "main": [[{ "node": "Check /healthz", "type": "main", "index": 0 }]] },
    "Check /healthz": { "main": [[{ "node": "Keep only failures", "type": "main", "index": 0 }]] },
    "Keep only failures": { "main": [[{ "node": "Alert Slack", "type": "main", "index": 0 }]] }
  },
  "settings": { "executionOrder": "v1" },
  "pinData": {},
  "meta": { "templateCredsSetupCompleted": false }
}
