{ "updatedAt": "2026-05-22T21:55:32.962Z", "createdAt": "2026-05-09T20:46:23.974Z", "id": "vdv7fILmnhQd9qL2", "name": "NFS Mount Watchdog + Auto-Heal v1.2", "description": null, "active": true, "isArchived": false, "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "minutes" } ] } }, "id": "d902559a-9099-4437-acf8-251ddc822dd8", "name": "Every 5 Minutes", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [ -2016, 240 ] }, { "parameters": { "command": "mountpoint -q /mnt/unraid/data && echo DATA_OK || echo DATA_MISSING; mountpoint -q /mnt/unraid/immich && echo IMMICH_OK || echo IMMICH_MISSING" }, "id": "81396ba5-a882-45c2-82f9-7838ce420d7f", "name": "Probe Mounts", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ -1792, 240 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": { "jsCode": "const out = ($input.first().json.stdout || '') + ($input.first().json.stderr || '');\nconst dataOk = out.includes('DATA_OK');\nconst immichOk = out.includes('IMMICH_OK');\nconst missing = [];\nif (!dataOk) missing.push({ mount: '/mnt/unraid/data', label: 'data',\n containers: ['ix-plex-plex-1', 'ix-audiobookshelf-audiobookshelf-1'] });\nif (!immichOk) missing.push({ mount: '/mnt/unraid/immich', label: 'immich',\n containers: ['ix-immich-immich-server-1', 'ix-immich-immich-machine-learning-1'] });\nreturn [{ json: { dataOk, immichOk, anyMissing: missing.length > 0, anyMissingStr: missing.length > 0 ? 'yes' : 'no', missing } }];\n" }, "id": "cbc19d0e-ca0c-41a8-8db6-a5f9754d68bf", "name": "Parse Mount Status", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -1568, 240 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 1 }, "conditions": [ { "id": "70384f84-9d15-4e45-b295-67202a39a227", "leftValue": "={{ $json.anyMissingStr }}", "rightValue": "yes", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "6c0f9f30-a2d3-4f85-a17d-99b215178652", "name": "Any Missing?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ -1344, 240 ] }, { "parameters": { "command": "sudo /usr/bin/bash /mnt/tank/docker/scripts/mount-unraid-nfs.sh" }, "id": "764c3d6c-f5fb-4ce7-9291-20718ad3b598", "name": "Run Mount Script", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ -1120, 128 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": {}, "id": "1510ac5b-873f-46d8-b86b-071ab0d6ca26", "name": "Wait 10s", "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ -896, 128 ], "webhookId": "4e89e8d0-7f08-4378-a2b7-63756665b039" }, { "parameters": { "command": "mountpoint -q /mnt/unraid/data && echo DATA_OK || echo DATA_MISSING; mountpoint -q /mnt/unraid/immich && echo IMMICH_OK || echo IMMICH_MISSING" }, "id": "4c4bbe24-d496-435e-ba81-02a91f1fb0b7", "name": "Re-probe Mounts", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ -672, 128 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": { "jsCode": "const out = ($input.first().json.stdout || '') + ($input.first().json.stderr || '');\nconst dataOk = out.includes('DATA_OK');\nconst immichOk = out.includes('IMMICH_OK');\n\nconst originalMissing = $('Parse Mount Status').first().json.missing || [];\n\nconst healed = originalMissing.filter(m =>\n (m.label === 'data' && dataOk) ||\n (m.label === 'immich' && immichOk)\n);\nconst stillDown = originalMissing.filter(m =>\n (m.label === 'data' && !dataOk) ||\n (m.label === 'immich' && !immichOk)\n);\n\nconst containersToRestart = [...new Set(healed.flatMap(m => m.containers))];\nconst restartCmd = containersToRestart.length > 0\n ? `sudo docker restart ${containersToRestart.join(' ')}`\n : 'echo NO_RESTART_NEEDED';\n\nreturn [{ json: {\n allHealed: stillDown.length === 0,\n allHealedStr: stillDown.length === 0 ? 'yes' : 'no',\n healed,\n stillDown,\n containersToRestart,\n restartCmd,\n originalMissing\n} }];\n" }, "id": "66a1d989-f0a2-4b5a-b7e0-1cb767d010e6", "name": "Parse Heal Result", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -448, 128 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 1 }, "conditions": [ { "id": "32b2dfd5-9958-45d4-94fc-410ce4d9543d", "leftValue": "={{ $json.allHealedStr }}", "rightValue": "yes", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "49d9ff7a-9227-405d-9887-a7537ce2b844", "name": "Healed?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ -224, 128 ] }, { "parameters": { "command": "={{ $json.restartCmd }}" }, "id": "69951482-d5e3-400c-9d3a-3790f671c225", "name": "Restart Containers", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ 0, 0 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": { "jsCode": "const d = $('Parse Heal Result').first().json;\nconst mountList = d.healed.map(m => ` • ${m.mount}`).join('\\n');\nconst containers = d.containersToRestart.join(', ') || 'none';\nconst body = `NFS mounts were missing and have been automatically remounted.\\n\\nRemounted:\\n${mountList}\\n\\nContainers restarted: ${containers}`;\nreturn [{ json: { title: '🔗 NFS Auto-Healed', message: body, priority: 5 } }];\n" }, "id": "c109df43-532a-4a49-af80-4897bb0b7c1a", "name": "Format Healed Notify", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 224, 0 ] }, { "parameters": { "message": "={{ $json.message }}", "additionalFields": { "priority": "={{ $json.priority }}", "title": "={{ $json.title }}" }, "options": { "contentType": "text/plain" } }, "id": "c8414235-8840-4302-a8e4-6644c3beadb9", "name": "Gotify (Healed)", "type": "n8n-nodes-base.gotify", "typeVersion": 1, "position": [ 448, 0 ], "credentials": { "gotifyApi": { "id": "K4p3mFrQrjDutIOU", "name": "Infrastructure" } } }, { "parameters": { "jsCode": "const d = $('Parse Heal Result').first().json;\nconst downList = d.stillDown.map(m => ` • ${m.mount}`).join('\\n');\nconst healedList = d.healed.length > 0\n ? `\\nPartially healed:\\n${d.healed.map(m => ' • ' + m.mount).join('\\n')}`\n : '';\nconst body = `NFS mount script ran but the following paths are still unavailable. Manual intervention required.\\n\\nStill down:\\n${downList}${healedList}\\n\\nCheck Serenity (10.5.30.5) is up and NFS service is running.`;\nreturn [{ json: { title: '🚨 NFS Down — Manual Fix Needed', message: body, priority: 9 } }];\n" }, "id": "ff65b3f1-ff81-48dd-a1e8-fd8adcef55d1", "name": "Format Escalation", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 0, 224 ] }, { "parameters": { "message": "={{ $json.message }}", "additionalFields": { "priority": "={{ $json.priority }}", "title": "={{ $json.title }}" }, "options": { "contentType": "text/plain" } }, "id": "39d131f2-f933-4253-92ce-9e312f881b16", "name": "Gotify (Escalation)", "type": "n8n-nodes-base.gotify", "typeVersion": 1, "position": [ 224, 224 ], "credentials": { "gotifyApi": { "id": "K4p3mFrQrjDutIOU", "name": "Infrastructure" } } } ], "connections": { "Every 5 Minutes": { "main": [ [ { "node": "Probe Mounts", "type": "main", "index": 0 } ] ] }, "Probe Mounts": { "main": [ [ { "node": "Parse Mount Status", "type": "main", "index": 0 } ] ] }, "Parse Mount Status": { "main": [ [ { "node": "Any Missing?", "type": "main", "index": 0 } ] ] }, "Any Missing?": { "main": [ [ { "node": "Run Mount Script", "type": "main", "index": 0 } ] ] }, "Run Mount Script": { "main": [ [ { "node": "Wait 10s", "type": "main", "index": 0 } ] ] }, "Wait 10s": { "main": [ [ { "node": "Re-probe Mounts", "type": "main", "index": 0 } ] ] }, "Re-probe Mounts": { "main": [ [ { "node": "Parse Heal Result", "type": "main", "index": 0 } ] ] }, "Parse Heal Result": { "main": [ [ { "node": "Healed?", "type": "main", "index": 0 } ] ] }, "Healed?": { "main": [ [ { "node": "Restart Containers", "type": "main", "index": 0 } ], [ { "node": "Format Escalation", "type": "main", "index": 0 } ] ] }, "Restart Containers": { "main": [ [ { "node": "Format Healed Notify", "type": "main", "index": 0 } ] ] }, "Format Healed Notify": { "main": [ [ { "node": "Gotify (Healed)", "type": "main", "index": 0 } ] ] }, "Format Escalation": { "main": [ [ { "node": "Gotify (Escalation)", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1", "binaryMode": "separate" }, "staticData": { "node:Every 5 Minutes": { "recurrenceRules": [] } }, "meta": { "templateCredsSetupCompleted": true }, "pinData": {}, "versionId": "68cca20d-5a60-446f-863f-abef2a12639f", "activeVersionId": "68cca20d-5a60-446f-863f-abef2a12639f", "versionCounter": 38, "triggerCount": 1, "shared": [ { "updatedAt": "2026-05-09T20:46:23.974Z", "createdAt": "2026-05-09T20:46:23.974Z", "role": "workflow:owner", "workflowId": "vdv7fILmnhQd9qL2", "projectId": "dxCRnBdX5uJizCGa", "project": { "updatedAt": "2026-05-06T01:10:37.484Z", "createdAt": "2026-05-06T01:08:07.721Z", "id": "dxCRnBdX5uJizCGa", "name": "Wilfred Fizzlepoof ", "type": "personal", "icon": null, "description": null, "creatorId": "47b2227f-2fc2-4a08-bd35-ea157b92df0d" } } ], "tags": [ { "updatedAt": "2026-05-06T22:38:51.660Z", "createdAt": "2026-05-06T22:38:51.660Z", "id": "0ETpkL5jJ5wwgt8k", "name": "monitoring" }, { "updatedAt": "2026-05-09T18:01:37.295Z", "createdAt": "2026-05-09T18:01:37.295Z", "id": "PYzt74cSdu6cC6P1", "name": "scheduled" }, { "updatedAt": "2026-05-09T18:06:13.298Z", "createdAt": "2026-05-09T18:06:13.298Z", "id": "urlD2YH1LZ32LY2d", "name": "infrastructure" } ], "activeVersion": { "updatedAt": "2026-05-22T21:55:32.963Z", "createdAt": "2026-05-22T21:55:32.963Z", "versionId": "68cca20d-5a60-446f-863f-abef2a12639f", "workflowId": "vdv7fILmnhQd9qL2", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "minutes" } ] } }, "id": "d902559a-9099-4437-acf8-251ddc822dd8", "name": "Every 5 Minutes", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [ -2016, 240 ] }, { "parameters": { "command": "mountpoint -q /mnt/unraid/data && echo DATA_OK || echo DATA_MISSING; mountpoint -q /mnt/unraid/immich && echo IMMICH_OK || echo IMMICH_MISSING" }, "id": "81396ba5-a882-45c2-82f9-7838ce420d7f", "name": "Probe Mounts", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ -1792, 240 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": { "jsCode": "const out = ($input.first().json.stdout || '') + ($input.first().json.stderr || '');\nconst dataOk = out.includes('DATA_OK');\nconst immichOk = out.includes('IMMICH_OK');\nconst missing = [];\nif (!dataOk) missing.push({ mount: '/mnt/unraid/data', label: 'data',\n containers: ['ix-plex-plex-1', 'ix-audiobookshelf-audiobookshelf-1'] });\nif (!immichOk) missing.push({ mount: '/mnt/unraid/immich', label: 'immich',\n containers: ['ix-immich-immich-server-1', 'ix-immich-immich-machine-learning-1'] });\nreturn [{ json: { dataOk, immichOk, anyMissing: missing.length > 0, anyMissingStr: missing.length > 0 ? 'yes' : 'no', missing } }];\n" }, "id": "cbc19d0e-ca0c-41a8-8db6-a5f9754d68bf", "name": "Parse Mount Status", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -1568, 240 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 1 }, "conditions": [ { "id": "70384f84-9d15-4e45-b295-67202a39a227", "leftValue": "={{ $json.anyMissingStr }}", "rightValue": "yes", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "6c0f9f30-a2d3-4f85-a17d-99b215178652", "name": "Any Missing?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ -1344, 240 ] }, { "parameters": { "command": "sudo /usr/bin/bash /mnt/tank/docker/scripts/mount-unraid-nfs.sh" }, "id": "764c3d6c-f5fb-4ce7-9291-20718ad3b598", "name": "Run Mount Script", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ -1120, 128 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": {}, "id": "1510ac5b-873f-46d8-b86b-071ab0d6ca26", "name": "Wait 10s", "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ -896, 128 ], "webhookId": "4e89e8d0-7f08-4378-a2b7-63756665b039" }, { "parameters": { "command": "mountpoint -q /mnt/unraid/data && echo DATA_OK || echo DATA_MISSING; mountpoint -q /mnt/unraid/immich && echo IMMICH_OK || echo IMMICH_MISSING" }, "id": "4c4bbe24-d496-435e-ba81-02a91f1fb0b7", "name": "Re-probe Mounts", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ -672, 128 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": { "jsCode": "const out = ($input.first().json.stdout || '') + ($input.first().json.stderr || '');\nconst dataOk = out.includes('DATA_OK');\nconst immichOk = out.includes('IMMICH_OK');\n\nconst originalMissing = $('Parse Mount Status').first().json.missing || [];\n\nconst healed = originalMissing.filter(m =>\n (m.label === 'data' && dataOk) ||\n (m.label === 'immich' && immichOk)\n);\nconst stillDown = originalMissing.filter(m =>\n (m.label === 'data' && !dataOk) ||\n (m.label === 'immich' && !immichOk)\n);\n\nconst containersToRestart = [...new Set(healed.flatMap(m => m.containers))];\nconst restartCmd = containersToRestart.length > 0\n ? `sudo docker restart ${containersToRestart.join(' ')}`\n : 'echo NO_RESTART_NEEDED';\n\nreturn [{ json: {\n allHealed: stillDown.length === 0,\n allHealedStr: stillDown.length === 0 ? 'yes' : 'no',\n healed,\n stillDown,\n containersToRestart,\n restartCmd,\n originalMissing\n} }];\n" }, "id": "66a1d989-f0a2-4b5a-b7e0-1cb767d010e6", "name": "Parse Heal Result", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -448, 128 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 1 }, "conditions": [ { "id": "32b2dfd5-9958-45d4-94fc-410ce4d9543d", "leftValue": "={{ $json.allHealedStr }}", "rightValue": "yes", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "49d9ff7a-9227-405d-9887-a7537ce2b844", "name": "Healed?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ -224, 128 ] }, { "parameters": { "command": "={{ $json.restartCmd }}" }, "id": "69951482-d5e3-400c-9d3a-3790f671c225", "name": "Restart Containers", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ 0, 0 ], "credentials": { "sshPassword": { "id": "0irY4e4WHwyMXKhA", "name": "SSH Password account" } }, "continueOnFail": true }, { "parameters": { "jsCode": "const d = $('Parse Heal Result').first().json;\nconst mountList = d.healed.map(m => ` • ${m.mount}`).join('\\n');\nconst containers = d.containersToRestart.join(', ') || 'none';\nconst body = `NFS mounts were missing and have been automatically remounted.\\n\\nRemounted:\\n${mountList}\\n\\nContainers restarted: ${containers}`;\nreturn [{ json: { title: '🔗 NFS Auto-Healed', message: body, priority: 5 } }];\n" }, "id": "c109df43-532a-4a49-af80-4897bb0b7c1a", "name": "Format Healed Notify", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 224, 0 ] }, { "parameters": { "message": "={{ $json.message }}", "additionalFields": { "priority": "={{ $json.priority }}", "title": "={{ $json.title }}" }, "options": { "contentType": "text/plain" } }, "id": "c8414235-8840-4302-a8e4-6644c3beadb9", "name": "Gotify (Healed)", "type": "n8n-nodes-base.gotify", "typeVersion": 1, "position": [ 448, 0 ], "credentials": { "gotifyApi": { "id": "K4p3mFrQrjDutIOU", "name": "Infrastructure" } } }, { "parameters": { "jsCode": "const d = $('Parse Heal Result').first().json;\nconst downList = d.stillDown.map(m => ` • ${m.mount}`).join('\\n');\nconst healedList = d.healed.length > 0\n ? `\\nPartially healed:\\n${d.healed.map(m => ' • ' + m.mount).join('\\n')}`\n : '';\nconst body = `NFS mount script ran but the following paths are still unavailable. Manual intervention required.\\n\\nStill down:\\n${downList}${healedList}\\n\\nCheck Serenity (10.5.30.5) is up and NFS service is running.`;\nreturn [{ json: { title: '🚨 NFS Down — Manual Fix Needed', message: body, priority: 9 } }];\n" }, "id": "ff65b3f1-ff81-48dd-a1e8-fd8adcef55d1", "name": "Format Escalation", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 0, 224 ] }, { "parameters": { "message": "={{ $json.message }}", "additionalFields": { "priority": "={{ $json.priority }}", "title": "={{ $json.title }}" }, "options": { "contentType": "text/plain" } }, "id": "39d131f2-f933-4253-92ce-9e312f881b16", "name": "Gotify (Escalation)", "type": "n8n-nodes-base.gotify", "typeVersion": 1, "position": [ 224, 224 ], "credentials": { "gotifyApi": { "id": "K4p3mFrQrjDutIOU", "name": "Infrastructure" } } } ], "connections": { "Every 5 Minutes": { "main": [ [ { "node": "Probe Mounts", "type": "main", "index": 0 } ] ] }, "Probe Mounts": { "main": [ [ { "node": "Parse Mount Status", "type": "main", "index": 0 } ] ] }, "Parse Mount Status": { "main": [ [ { "node": "Any Missing?", "type": "main", "index": 0 } ] ] }, "Any Missing?": { "main": [ [ { "node": "Run Mount Script", "type": "main", "index": 0 } ] ] }, "Run Mount Script": { "main": [ [ { "node": "Wait 10s", "type": "main", "index": 0 } ] ] }, "Wait 10s": { "main": [ [ { "node": "Re-probe Mounts", "type": "main", "index": 0 } ] ] }, "Re-probe Mounts": { "main": [ [ { "node": "Parse Heal Result", "type": "main", "index": 0 } ] ] }, "Parse Heal Result": { "main": [ [ { "node": "Healed?", "type": "main", "index": 0 } ] ] }, "Healed?": { "main": [ [ { "node": "Restart Containers", "type": "main", "index": 0 } ], [ { "node": "Format Escalation", "type": "main", "index": 0 } ] ] }, "Restart Containers": { "main": [ [ { "node": "Format Healed Notify", "type": "main", "index": 0 } ] ] }, "Format Healed Notify": { "main": [ [ { "node": "Gotify (Healed)", "type": "main", "index": 0 } ] ] }, "Format Escalation": { "main": [ [ { "node": "Gotify (Escalation)", "type": "main", "index": 0 } ] ] } }, "authors": "Wilfred Fizzlepoof", "name": null, "description": null, "autosaved": false, "workflowPublishHistory": [ { "createdAt": "2026-05-22T21:55:33.007Z", "id": 327, "workflowId": "vdv7fILmnhQd9qL2", "versionId": "68cca20d-5a60-446f-863f-abef2a12639f", "event": "deactivated", "userId": "47b2227f-2fc2-4a08-bd35-ea157b92df0d" }, { "createdAt": "2026-05-22T21:55:33.028Z", "id": 328, "workflowId": "vdv7fILmnhQd9qL2", "versionId": "68cca20d-5a60-446f-863f-abef2a12639f", "event": "activated", "userId": "47b2227f-2fc2-4a08-bd35-ea157b92df0d" } ] } }