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 →
{
"name": "Self-healing containers with escalation",
"nodes": [
{
"parameters": {
"actions": [
"die"
],
"filters": {
"label": "selfheal=true"
},
"eventTypes": [
"container"
],
"catchUp": true
},
"id": "b1000000-0000-4000-8000-000000000001",
"name": "A container died",
"type": "n8n-nodes-docker-api.dockerTrigger",
"typeVersion": 1,
"position": [
-656,
0
],
"notes": "Opt in per container with the label selfheal=true, so this never touches something you did not mean it to. Catch-up is on: a container that dies while n8n itself is restarting is still handled, which is exactly when it tends to happen."
},
{
"parameters": {
"operation": "getLogs",
"containerId": "={{ $json.name }}",
"tail": 50,
"timestamps": true,
"retryPolicy": {},
"resource": "container",
"stream": "both",
"sinceMinutes": 0
},
"id": "b1000000-0000-4000-8000-000000000002",
"name": "Capture logs BEFORE restarting",
"type": "n8n-nodes-docker-api.docker",
"typeVersion": 1,
"position": [
-448,
0
],
"onError": "continueRegularOutput",
"notes": "Taken first, deliberately. Restarting can rotate or discard the very output that explains the crash, so the evidence is collected while it still exists."
},
{
"parameters": {
"operation": "inspect",
"containerId": "={{ $('A container died').item.json.name }}",
"retryPolicy": {},
"resource": "container"
},
"id": "b1000000-0000-4000-8000-000000000003",
"name": "How many times has it restarted?",
"type": "n8n-nodes-docker-api.docker",
"typeVersion": 1,
"position": [
-224,
0
]
},
{
"parameters": {
"jsCode": "// Restarting forever hides a real fault. After a few attempts, stop trying and\n// tell a human instead \u2014 a container that has died five times in a row is not\n// going to be fixed by a sixth restart.\nconst MAX_RESTARTS = 5;\n\nconst inspected = $input.first().json;\nconst event = $('A container died').first().json;\nconst logs = $('Capture logs BEFORE restarting').first().json;\n\nconst restartCount = inspected.state?.restartCount ?? 0;\nconst logLines = (logs.logs ?? [])\n .map((l) => `[${l.stream}] ${l.message}`)\n .join('\\n');\n\nreturn [{\n json: {\n container: event.name,\n image: event.image ?? inspected.image ?? null,\n exitCode: event.attributes?.exitCode ?? null,\n diedAt: event.time ?? null,\n restartCount,\n giveUp: restartCount >= MAX_RESTARTS,\n logTail: logLines || '(no output captured)',\n },\n}];"
},
"id": "b1000000-0000-4000-8000-000000000004",
"name": "Worth another try?",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
0,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "g1",
"leftValue": "={{ $json.giveUp }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "b1000000-0000-4000-8000-000000000005",
"name": "Under the restart limit?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
224,
0
]
},
{
"parameters": {
"operation": "start",
"containerId": "={{ $json.container }}",
"retryPolicy": {},
"resource": "container"
},
"id": "b1000000-0000-4000-8000-000000000006",
"name": "Restart it",
"type": "n8n-nodes-docker-api.docker",
"typeVersion": 1,
"position": [
448,
-128
]
},
{
"parameters": {
"operation": "waitForState",
"containerId": "={{ $('Worth another try?').item.json.container }}",
"retryPolicy": {},
"resource": "container",
"targetState": "running",
"waitTimeout": 60
},
"id": "b1000000-0000-4000-8000-000000000007",
"name": "Confirm it stayed up",
"type": "n8n-nodes-docker-api.docker",
"typeVersion": 1,
"position": [
672,
-128
],
"notes": "Starting is not the same as staying started. If the container has a health check, change the target to 'healthy' for a much stronger guarantee."
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "r1",
"leftValue": "={{ $json.reached }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "b1000000-0000-4000-8000-000000000008",
"name": "Did it recover?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
880,
-128
]
},
{
"parameters": {},
"id": "b1000000-0000-4000-8000-000000000009",
"name": "Recovered",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
1104,
-240
],
"notes": "Worth a quiet notification rather than a loud one \u2014 but do send something. A container silently restarting nightly is a fault you want to know about eventually."
},
{
"parameters": {},
"id": "b1000000-0000-4000-8000-000000000010",
"name": "ALERT: restart did not hold",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
1104,
-16
],
"notes": "Replace with Slack, Discord or email. The item already carries container, exitCode, restartCount and logTail \u2014 everything needed to triage without opening a terminal."
},
{
"parameters": {},
"id": "b1000000-0000-4000-8000-000000000011",
"name": "ALERT: giving up, needs a human",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
448,
144
],
"notes": "The restart limit was reached. Escalate loudly and stop restarting: past this point a restart loop only buries the cause and burns CPU."
},
{
"parameters": {
"content": "## \ud83c\udff7\ufe0f Containers opt in\n\nAdd the label `selfheal=true` to any container you want restarted automatically:\n\n```\ndocker run -l selfheal=true ...\n```\n\nNothing else is touched, so this can never restart something you stopped on purpose.",
"height": 598,
"width": 204,
"color": 4
},
"id": "b9000000-0000-4000-8000-000000000001",
"name": "Opt in per container",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-704,
-432
]
},
{
"parameters": {
"content": "## \ud83d\udd0d Logs are taken first\n\nDeliberately **before** the restart. Restarting can rotate or discard the very output that explains the crash.\n\nThe alert carries the last 50 lines, each labelled with the stream it came from.",
"height": 438,
"width": 840,
"color": 5
},
"id": "b9000000-0000-4000-8000-000000000002",
"name": "Evidence before action",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-480,
-272
]
},
{
"parameters": {
"content": "## \u2705 Starting is not recovering\n\nIf your container defines a health check, change **Confirm it stayed up** to wait for `healthy` instead of `running` \u2014 a much stronger guarantee.",
"height": 294,
"width": 588,
"color": 6
},
"id": "b9000000-0000-4000-8000-000000000003",
"name": "Confirm it held",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
432,
-272
]
},
{
"parameters": {
"content": "\n\n\n\n\n\n\n\n\n## \ud83d\uded1 It gives up after 5 tries\n\nA container that has died five times will not be fixed by a sixth restart, and a restart loop buries the cause while burning CPU.\n\nChange the limit in **Worth another try?**",
"height": 424,
"width": 420,
"color": 3
},
"id": "b9000000-0000-4000-8000-000000000004",
"name": "Know when to stop",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
384,
112
]
}
],
"connections": {
"A container died": {
"main": [
[
{
"node": "Capture logs BEFORE restarting",
"type": "main",
"index": 0
}
]
]
},
"Capture logs BEFORE restarting": {
"main": [
[
{
"node": "How many times has it restarted?",
"type": "main",
"index": 0
}
]
]
},
"How many times has it restarted?": {
"main": [
[
{
"node": "Worth another try?",
"type": "main",
"index": 0
}
]
]
},
"Worth another try?": {
"main": [
[
{
"node": "Under the restart limit?",
"type": "main",
"index": 0
}
]
]
},
"Under the restart limit?": {
"main": [
[
{
"node": "Restart it",
"type": "main",
"index": 0
}
],
[
{
"node": "ALERT: giving up, needs a human",
"type": "main",
"index": 0
}
]
]
},
"Restart it": {
"main": [
[
{
"node": "Confirm it stayed up",
"type": "main",
"index": 0
}
]
]
},
"Confirm it stayed up": {
"main": [
[
{
"node": "Did it recover?",
"type": "main",
"index": 0
}
]
]
},
"Did it recover?": {
"main": [
[
{
"node": "Recovered",
"type": "main",
"index": 0
}
],
[
{
"node": "ALERT: restart did not hold",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Self-healing containers with escalation. Uses n8n-nodes-docker-api. Event-driven trigger; 15 nodes.
Source: https://github.com/ramygamal231/n8n-nodes-docker-api/blob/main/templates/self-healing-containers.json — 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.
This n8n workflow provides automated CI/CD testing for Kubernetes applications using KinD (Kubernetes in Docker). It creates temporary infrastructure, runs tests, and cleans up everything automaticall
Auto-update a container when a new image is published. Uses n8n-nodes-docker-api. Scheduled trigger; 18 nodes.
This automated n8n workflow delivers an instant DevOps toolkit by installing Docker, K3s, Jenkins, Grafana, and more on a Linux server within 10 seconds. It optimizes performance, enhances security, a
This workflow backs up all n8n workflows and encrypted credentials from a self-hosted Docker instance to a GitHub repository on a schedule, and provides a Basic Auth–protected form to restore workflow
This template lets you selectively import n8n workflows from a GitHub repository, even when your repository uses deeply nested folder structures.