Repository: hashgraph/hedera-local-node (ts)

Issue:
Stopping the dockerized local node affects unrelated networks
### Description

When stopping the Hedera Local Node (npm run stop) or retrying to start the container, the following command is used:

```
docker network prune -f
```

This command affects all networks started by the Docker instance, not just the ones started by the Hedera Local Node repository.

### Steps to reproduce

1. Ensure you have some development networks configured in your local environment. Make sure that some of these networks are not currently in use.
2. Start the Hedera Local Node.
3. Stop the Hedera Local Node.
4. Investigate which networks are affected by the Docker prune operation.

You will likely observe that unrelated networks are impacted by the execution of our script.

### Additional context

_No response_

### Hedera network

other

### Version

v0.1.0

### Operating system

None
```[tasklist]
### Tasks
- [ ] https://github.com/hashgraph/hedera-local-node/pull/686
```


Pull request description:
fix: removing only networks started by hedera instead of global prune
**Description**:
https://github.com/hashgraph/hedera-local-node/issues/685#issue-2388853878

**Related issue(s)**:

Fixes #685 

**Notes for reviewer**:

**Checklist**

- [ ] Documented (Code comments, README, etc.)
- [ ] Tested (unit, integration, etc.)


Patch:
diff --git a/.env b/.env
index 2f9ee10..5e6ef80 100644
--- a/.env
+++ b/.env
@@ -4,8 +4,8 @@ NETWORK_NODE_IMAGE_PREFIX=gcr.io/hedera-registry/
 NETWORK_NODE_IMAGE_NAME=main-network-node
 
 #### Image Tags/Hashes ####
-NETWORK_NODE_IMAGE_TAG=0.51.3
-HAVEGED_IMAGE_TAG=0.51.3
+NETWORK_NODE_IMAGE_TAG=0.52.0-alpha.4
+HAVEGED_IMAGE_TAG=0.52.0-alpha.4
 
 #### Java Process Settings ####
 PLATFORM_JAVA_HEAP_MIN=256m
@@ -29,7 +29,7 @@ PYTHON_VERSION=python3.7
 
 #### MirrorNode Prefixes & Tags ####
 MIRROR_IMAGE_PREFIX=gcr.io/mirrornode/
-MIRROR_IMAGE_TAG=0.108.0-beta1
+MIRROR_IMAGE_TAG=0.108.0
 
 #### MirrorNode settings ####
 MIRROR_POSTGRES_IMAGE=postgres:14-alpine
@@ -45,7 +45,7 @@ MINIO_ROOT_PASSWORD=minioadmin
 
 #### JSON RPC Relay Prefixes & Tags ####
 RELAY_IMAGE_PREFIX=ghcr.io/hashgraph/
-RELAY_IMAGE_TAG=0.50.1
+RELAY_IMAGE_TAG=0.51.1
 
 #### JSON RPC Relay limits ####
 RELAY_MEM_LIMIT=768m
diff --git a/README.md b/README.md
index e50d9d2..7ce1e73 100644
--- a/README.md
+++ b/README.md
@@ -90,6 +90,7 @@ npm install && npm install -g
 
 > **_NOTE:_**  If you want to use any of the CLI options listed below, you'd need to pass `--` after `npm run start` (for example) and then specify the wanted option. For example, if you want to start in detached mode, you can use `npm run start -- -d`
 
+> **_WARNING:_** While stopping the networks, we will first list all Docker networks with the `hedera-` prefix in their names. This operation may affect not only the networks initiated by the `npm run start` command from this repository but also any other networks you have created with this prefix. Network termination can be triggered both by a direct `npm run stop` call and by the `npm run start` script if the initial startup process fails and failover recovery is activated. One of the recovery steps includes attempting to close all previously started networks with the `hedera-` prefix.
 
 ## Using hedera-local
 
diff --git a/docker-compose-state-migration.yml b/docker-compose-state-migration.yml
index 771dfd1..233abc1 100644
--- a/docker-compose-state-migration.yml
+++ b/docker-compose-state-migration.yml
@@ -356,7 +356,7 @@ services:
 
 networks:
   network-node-bridge:
-    name: network-node-bridge
+    name: hedera-network-node-bridge
     driver: bridge
     ipam:
       driver: default
@@ -365,10 +365,10 @@ networks:
           ip_range: 172.27.0.0/24
           gateway: 172.27.0.254
   mirror-node:
-    name: mirror-node
+    name: hedera-mirror-node
     driver: bridge
   cloud-storage:
-    name: cloud-storage
+    name: hedera-cloud-storage
     driver: bridge
 
 volumes:
diff --git a/docker-compose.evm.yml b/docker-compose.evm.yml
index 3c1ca83..b6fc180 100644
--- a/docker-compose.evm.yml
+++ b/docker-compose.evm.yml
@@ -8,6 +8,7 @@ services:
         "echo 'Deleting old recordStream files.';find /records/ -type f -delete;echo 'This container is intentionally disabled by the EVM profile.';"
       ]
     command: []
+
   account-balances-uploader:
     restart: "no"
     entrypoint:
@@ -17,6 +18,7 @@ services:
         "echo 'Deleting old accountBalances files.';find /balances/ -type f -delete;echo 'This container is intentionally disabled by the EVM profile.';"
       ]
     command: []
+
   record-sidecar-uploader:
     restart: "no"
     entrypoint:
@@ -26,6 +28,7 @@ services:
         "echo 'Deleting old sidecars files.';find /sidecar-files/ -type f -delete;echo 'This container is intentionally disabled by the EVM profile.';"
       ]
     command: []
+
   minio:
     restart: "no"
     entrypoint:
@@ -34,6 +37,7 @@ services:
         "This container is intentionally disabled by the EVM profile."
       ]
     command: []
+
   importer:
     user: root
     volumes:
diff --git a/docker-compose.yml b/docker-compose.yml
index 2ec9fb7..50532bc 100755
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -288,6 +288,8 @@ services:
     environment:
       DOCKER_LOCAL_MIRROR_NODE_MENU_NAME: ${DOCKER_LOCAL_MIRROR_NODE_MENU_NAME}
       DOCKER_LOCAL_MIRROR_NODE_URL: ${DOCKER_LOCAL_MIRROR_NODE_URL}
+    networks:
+      - mirror-node
 
   web3:
     image: "${MIRROR_IMAGE_PREFIX}hedera-mirror-web3:${MIRROR_WEB3_IMAGE_TAG:-${MIRROR_IMAGE_TAG}}"
@@ -501,7 +503,7 @@ services:
 
 networks:
   network-node-bridge:
-    name: network-node-bridge
+    name: hedera-network-node
     driver: bridge
     ipam:
       driver: default
@@ -510,10 +512,10 @@ networks:
           ip_range: 172.27.0.0/24
           gateway: 172.27.0.254
   mirror-node:
-    name: mirror-node
+    name: hedera-mirror-node
     driver: bridge
   cloud-storage:
-    name: cloud-storage
+    name: hedera-cloud-storage
     driver: bridge
 
 volumes:
diff --git a/package-lock.json b/package-lock.json
index 1d5b8bd..6d65ca6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@hashgraph/hedera-local",
-  "version": "2.27.0",
+  "version": "2.27.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@hashgraph/hedera-local",
-      "version": "2.27.0",
+      "version": "2.27.1",
       "license": "Apache-2.0",
       "dependencies": {
         "@hashgraph/sdk": "^2.48.1",
@@ -18,7 +18,7 @@
         "dotenv": "^16.4.5",
         "ethers": "^6.13.1",
         "js-yaml": "^4.1.0",
-        "rimraf": "^5.0.7",
+        "rimraf": "^6.0.0",
         "semver": "^7.6.2",
         "shelljs": "^0.8.5",
         "ts-mocha": "^10.0.0",
@@ -46,6 +46,7 @@
         "@typescript-eslint/eslint-plugin": "^7.14.1",
         "@typescript-eslint/parser": "^7.14.1",
         "chai": "^4.3.6",
+        "conventional-changelog-conventionalcommits": "^8.0.0",
         "eslint": "^8.51.0",
         "eslint-config-airbnb": "^19.0.4",
         "eslint-config-prettier": "^9.1.0",
@@ -3863,6 +3864,18 @@
         "node": ">=18"
       }
     },
+    "node_modules/conventional-changelog-conventionalcommits": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-8.0.0.tgz",
+      "integrity": "sha512-eOvlTO6OcySPyyyk8pKz2dP4jjElYunj9hn9/s0OB+gapTO8zwS9UQWrZ1pmF2hFs3vw1xhonOLGcGjy/zgsuA==",
+      "dev": true,
+      "dependencies": {
+        "compare-func": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/conventional-changelog-writer": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz",
@@ -5675,9 +5688,9 @@
       }
     },
     "node_modules/foreground-child": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
-      "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
+      "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==",
       "dependencies": {
         "cross-spawn": "^7.0.0",
         "signal-exit": "^4.0.1"
@@ -7139,14 +7152,14 @@
       }
     },
     "node_modules/jackspeak": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
-      "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz",
+      "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==",
       "dependencies": {
         "@isaacs/cliui": "^8.0.2"
       },
       "engines": {
-        "node": ">=14"
+        "node": "20 || >=22"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
@@ -7560,6 +7573,7 @@
       "version": "10.2.2",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
       "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
+      "dev": true,
       "engines": {
         "node": "14 || >=16.14"
       }
@@ -7793,9 +7807,9 @@
       }
     },
     "node_modules/minipass": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
-      "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
       "engines": {
         "node": ">=16 || 14 >=14.17"
       }
@@ -11217,6 +11231,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
+      "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
+    },
     "node_modules/parent-module": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -11310,20 +11329,28 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
     },
     "node_modules/path-scurry": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
-      "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
+      "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
       "dependencies": {
-        "lru-cache": "^9.1.1 || ^10.0.0",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+        "lru-cache": "^11.0.0",
+        "minipass": "^7.1.2"
       },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
+        "node": "20 || >=22"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/path-scurry/node_modules/lru-cache": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz",
+      "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
     "node_modules/path-to-regexp": {
       "version": "6.2.2",
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz",
@@ -12128,17 +12155,17 @@
       "integrity": "sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ=="
     },
     "node_modules/rimraf": {
-      "version": "5.0.7",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz",
-      "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.0.tgz",
+      "integrity": "sha512-u+yqhM92LW+89cxUQK0SRyvXYQmyuKHx0jkx4W7KfwLGLqJnQM5031Uv1trE4gB9XEXBM/s6MxKlfW95IidqaA==",
       "dependencies": {
-        "glob": "^10.3.7"
+        "glob": "^11.0.0"
       },
       "bin": {
         "rimraf": "dist/esm/bin.mjs"
       },
       "engines": {
-        "node": ">=14.18"
+        "node": "20 || >=22"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
@@ -12153,35 +12180,36 @@
       }
     },
     "node_modules/rimraf/node_modules/glob": {
-      "version": "10.3.10",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
-      "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
+      "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==",
       "dependencies": {
         "foreground-child": "^3.1.0",
-        "jackspeak": "^2.3.5",
-        "minimatch": "^9.0.1",
-        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
-        "path-scurry": "^1.10.1"
+        "jackspeak": "^4.0.1",
+        "minimatch": "^10.0.0",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^2.0.0"
       },
       "bin": {
         "glob": "dist/esm/bin.mjs"
       },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
+        "node": "20 || >=22"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/rimraf/node_modules/minimatch": {
-      "version": "9.0.3",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
-      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
+      "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
       "dependencies": {
         "brace-expansion": "^2.0.1"
       },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
+        "node": "20 || >=22"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
diff --git a/package.json b/package.json
index 821bc20..bd68d4e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@hashgraph/hedera-local",
-  "version": "2.27.0",
+  "version": "2.27.1",
   "description": "Developer tooling for running Local Hedera Network (Consensus + Mirror Nodes).",
   "main": "index.ts",
   "scripts": {
@@ -50,6 +50,7 @@
     "@typescript-eslint/eslint-plugin": "^7.14.1",
     "@typescript-eslint/parser": "^7.14.1",
     "chai": "^4.3.6",
+    "conventional-changelog-conventionalcommits": "^8.0.0",
     "eslint": "^8.51.0",
     "eslint-config-airbnb": "^19.0.4",
     "eslint-config-prettier": "^9.1.0",
@@ -72,7 +73,7 @@
     "dotenv": "^16.4.5",
     "ethers": "^6.13.1",
     "js-yaml": "^4.1.0",
-    "rimraf": "^5.0.7",
+    "rimraf": "^6.0.0",
     "semver": "^7.6.2",
     "shelljs": "^0.8.5",
     "ts-mocha": "^10.0.0",
diff --git a/src/configuration/local.json b/src/configuration/local.json
index 73c9b4d..5099056 100644
--- a/src/configuration/local.json
+++ b/src/configuration/local.json
@@ -1,9 +1,9 @@
 {
   "imageTagConfiguration": [
-    {"key": "NETWORK_NODE_IMAGE_TAG", "value": "0.51.3"},
-    {"key": "HAVEGED_IMAGE_TAG", "value": "0.51.3"},
-    {"key": "MIRROR_IMAGE_TAG", "value": "0.108.0-beta1"},
-    {"key": "RELAY_IMAGE_TAG", "value": "0.50.2"},
+    {"key": "NETWORK_NODE_IMAGE_TAG", "value": "0.52.0-alpha.5"},
+    {"key": "HAVEGED_IMAGE_TAG", "value": "0.52.0-alpha.5"},
+    {"key": "MIRROR_IMAGE_TAG", "value": "0.108.0"},
+    {"key": "RELAY_IMAGE_TAG", "value": "0.51.1"},
     {"key": "MIRROR_NODE_EXPLORER_IMAGE_TAG", "value": "24.4.0"}
   ],
   "envConfiguration": [
diff --git a/src/constants.ts b/src/constants.ts
index 172a617..5d29eff 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -36,6 +36,8 @@ export const CONTAINERS = [
     },
 ];
 
+export const NETWORK_PREFIX = 'hedera-';
+
 export const CONSENSUS_NODE_LABEL = "network-node";
 export const MIRROR_NODE_LABEL = "mirror-node-rest";
 export const RELAY_LABEL = "json-rpc-relay";
diff --git a/src/services/DockerService.ts b/src/services/DockerService.ts
index 5fb1b6b..03196bb 100644
--- a/src/services/DockerService.ts
+++ b/src/services/DockerService.ts
@@ -35,6 +35,7 @@ import detectPort from 'detect-port';
 import * as dotenv from 'dotenv';
 import { CLIOptions } from '../types/CLIOptions';
 import path from 'path';
+import { SafeDockerNetworkRemover } from '../utils/SafeDockerNetworkRemover';
 
 dotenv.config();
 
@@ -90,7 +91,7 @@ export class DockerService implements IService{
 
     /**
      * Returns the null output path depending on the operating system.
-     * 
+     *
      * @public
      * @returns {string} - The null output path.
      */
@@ -391,7 +392,7 @@ export class DockerService implements IService{
         shell.exec(`docker compose down -v --remove-orphans 2>${nullOutput}`);
         this.logger.trace('Cleaning the volumes and temp files...', stateName);
         shell.exec(`rm -rf network-logs/* >${nullOutput} 2>&1`);
-        shell.exec(`docker network prune -f 2>${nullOutput}`);
+        SafeDockerNetworkRemover.removeAll();
         this.logger.info(`${LOADING} Trying to startup again...`, stateName);
     }
 }
diff --git a/src/state/StopState.ts b/src/state/StopState.ts
index 8454032..16f2a1d 100644
--- a/src/state/StopState.ts
+++ b/src/state/StopState.ts
@@ -36,6 +36,7 @@ import {
 } from '../constants';
 import { CLIOptions } from '../types/CLIOptions';
 import { CLIService } from '../services/CLIService';
+import { SafeDockerNetworkRemover } from '../utils/SafeDockerNetworkRemover';
 
 export class StopState implements IState {
     /**
@@ -101,7 +102,7 @@ export class StopState implements IState {
         shell.exec(`rm -rf network-logs/* >${nullOutput} 2>&1`);
         this.logger.trace(`Working dir is ${this.cliOptions.workDir}`, this.stateName);
         shell.exec(`rm -rf "${join(this.cliOptions.workDir, 'network-logs')}" >${nullOutput} 2>&1`);
-        shell.exec(`docker network prune -f 2>${nullOutput}`);
+        SafeDockerNetworkRemover.removeAll();
         shell.cd(rootPath);
         this.logger.info(STOP_STATE_STOPPED_MESSAGE, this.stateName);
         this.observer?.update(EventType.Finish);
diff --git a/src/utils/SafeDockerNetworkRemover.ts b/src/utils/SafeDockerNetworkRemover.ts
new file mode 100644
index 0000000..a8ed181
--- /dev/null
+++ b/src/utils/SafeDockerNetworkRemover.ts
@@ -0,0 +1,57 @@
+/*-
+ *
+ * Hedera Local Node
+ *
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import shell from 'shelljs';
+import { IS_WINDOWS, NETWORK_PREFIX } from '../constants';
+
+/**
+ * Checks if the given string is a valid Docker network ID.
+ * A valid Docker network ID is a 12-character hexadecimal string.
+ *
+ * @param {string} id - The string to be validated as a Docker network ID.
+ * @returns {boolean} - Returns true if the string is a valid Docker network ID, false otherwise.
+ *
+ * @example
+ * const id = "89ded1eca1d5";
+ * console.log(isCorrectDockerId(id)); // Output: true
+ *
+ * @example
+ * const invalidId = "invalidID123";
+ * console.log(isCorrectDockerId(invalidId)); // Output: false
+ */
+const isCorrectDockerId = (id: string) => id.trim() !== '' && /^[a-f0-9]{12}$/.test(id);
+
+/**
+ * Provides utility methods for safe networks removal.
+ */
+export class SafeDockerNetworkRemover {
+  /**
+   * Removes all the networks started by docker compose. Only networks with the "hedera-" prefix will be affected.
+   */
+  public static removeAll() {
+    const result = shell.exec(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`);
+    if (!result || result.stderr !== '') {
+      return;
+    }
+    result.stdout.split('\n').filter(isCorrectDockerId).forEach((id) => {
+      shell.exec(`docker network rm ${id} -f 2>${IS_WINDOWS ? 'null' : '/dev/null'}`);
+    });
+  }
+}


Test patch:
diff --git a/test/unit/states/InitState.spec.ts b/test/unit/states/InitState.spec.ts
index 73aa056..a3da907 100644
--- a/test/unit/states/InitState.spec.ts
+++ b/test/unit/states/InitState.spec.ts
@@ -21,6 +21,7 @@
 import { expect } from 'chai';
 import fs from 'fs';
 import yaml from 'js-yaml';
+import { after } from "mocha";
 import { join } from 'path';
 import { SinonSandbox, SinonSpy, SinonStub, SinonStubbedInstance } from 'sinon';
 import {
@@ -249,6 +250,11 @@ describe('InitState tests', () => {
             onStartStub.restore()
         })
 
+        after(() => {
+            fsReadFileSync.restore()
+            ymlLoad.restore()
+        })
+
         it('should execute "prepareWorkDirectory" as expected', async () => {
             prepareWorkDirectoryStub.restore()
 
diff --git a/test/unit/states/StopState.spec.ts b/test/unit/states/StopState.spec.ts
index bed9bab..cd60010 100644
--- a/test/unit/states/StopState.spec.ts
+++ b/test/unit/states/StopState.spec.ts
@@ -24,6 +24,7 @@ import { StopState } from '../../../src/state/StopState';
 import {
   DOCKER_CLEANING_VALUMES_MESSAGE,
   DOCKER_STOPPING_CONTAINERS_MESSAGE,
+  NETWORK_PREFIX,
   STOP_STATE_INIT_MESSAGE,
   STOP_STATE_ON_START_MESSAGE,
   STOP_STATE_STOPPED_MESSAGE,
@@ -79,6 +80,11 @@ describe('StopState tests', () => {
 
     it('should execute onStart properly', async () => {
         const { shellCDStub, shellExecStub }= shellTest;
+        const network = { name: 'hedera-cloud-storage', id: '89ded1eca1d5' };
+        shellExecStub
+          .withArgs(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`)
+          .returns({ stderr: '', stdout: network.id });
+
         await stopState.onStart();
 
         // loggin messages
@@ -100,7 +106,7 @@ describe('StopState tests', () => {
         testSandbox.assert.calledWith(shellExecStub, 'docker compose down -v --remove-orphans 2>/dev/null');
         testSandbox.assert.calledWith(shellExecStub, 'rm -rf network-logs/* >/dev/null 2>&1');
         testSandbox.assert.calledWith(shellExecStub, 'rm -rf "testDir/network-logs" >/dev/null 2>&1');
-        testSandbox.assert.calledWith(shellExecStub, 'docker network prune -f 2>/dev/null');
+        testSandbox.assert.calledWith(shellExecStub, `docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`);
 
         expect(processTest.processCWDStub.calledOnce).to.be.true;
     })
diff --git a/test/unit/utils/SafeDockerNetworkRemover.spec.ts b/test/unit/utils/SafeDockerNetworkRemover.spec.ts
new file mode 100644
index 0000000..94393ce
--- /dev/null
+++ b/test/unit/utils/SafeDockerNetworkRemover.spec.ts
@@ -0,0 +1,109 @@
+/*-
+ *
+ * Hedera Local Node
+ *
+ * Copyright (C) 2024 Hedera Hashgraph, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { expect } from 'chai';
+import sinon from 'sinon';
+import { SafeDockerNetworkRemover } from '../../../src/utils/SafeDockerNetworkRemover';
+import { IS_WINDOWS, NETWORK_PREFIX } from '../../../src/constants';
+import { getTestBed } from '../testBed';
+import { load } from 'js-yaml';
+import { readdirSync, readFileSync } from 'fs';
+import { join } from "path";
+
+describe('SafeDockerNetworkRemover', () => {
+  let shellExecStub: sinon.SinonStub;
+
+  before(() => {
+    let {
+      shellStubs
+    } = getTestBed({
+      workDir: 'testDir',
+    });
+
+    shellExecStub = shellStubs.shellExecStub;
+  });
+
+  after(() => {
+    shellExecStub.restore();
+  });
+
+  describe('removeByName', () => {
+    it('should not proceed if docker network ls command fails', () => {
+      shellExecStub.withArgs(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`).returns({ stderr: 'error' });
+
+      SafeDockerNetworkRemover.removeAll();
+      expect(shellExecStub.calledOnce).to.be.true;
+      expect(shellExecStub.calledWith(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`)).to.be.true;
+    });
+
+    it('should not proceed if docker network ls command returns no result', () => {
+      shellExecStub.withArgs(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`).returns({ stdout: '', stderr: '' });
+
+      SafeDockerNetworkRemover.removeAll();
+      expect(shellExecStub.calledWith(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`)).to.be.true;
+    });
+
+    it('should remove valid Docker network IDs', () => {
+      shellExecStub.withArgs(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`).returns({ stdout: '89ded1eca1d5\ninvalidID123\n', stderr: '' });
+      shellExecStub.withArgs(`docker network rm 89ded1eca1d5 -f 2>${IS_WINDOWS ? 'null' : '/dev/null'}`).returns({});
+
+      SafeDockerNetworkRemover.removeAll();
+      expect(shellExecStub.calledWith(`docker network rm 89ded1eca1d5 -f 2>${IS_WINDOWS ? 'null' : '/dev/null'}`)).to.be.true;
+    });
+
+    it('should not remove invalid Docker network IDs', () => {
+      shellExecStub.withArgs(`docker network ls --filter name=${NETWORK_PREFIX} --format "{{.ID}}"`).returns({ stdout: 'invalidID123\nanotherInvalid\n', stderr: '' });
+
+      SafeDockerNetworkRemover.removeAll();
+      expect(shellExecStub.calledWith(`docker network rm invalidID123 -f 2>${IS_WINDOWS ? 'null' : '/dev/null'}`)).to.be.false;
+      expect(shellExecStub.calledWith(`docker network rm anotherInvalid -f 2>${IS_WINDOWS ? 'null' : '/dev/null'}`)).to.be.false;
+    });
+  });
+
+  describe('config check', () => {
+    it('check if all of the networks from composer yaml files are listed in the NETWORK_NAMES const', () => {
+      const relativePath = '../../..';
+      const files = readdirSync(join(__dirname, relativePath)).filter(name => /^docker-compose.*\.yml$/.test(name));
+      for (const file of files) {
+        const data = readFileSync(join(__dirname, `${relativePath}/${file}`));
+        const config = load(data.toString());
+        for (const network of Object.values(config.networks || {}).map((network: any) => network.name.trim())) {
+          expect(network.startsWith(NETWORK_PREFIX), `Network '${network}' does not start with the prefix '${NETWORK_PREFIX}'. It won't be removed by 'npm run stop'`).to.be.true;
+        }
+      }
+    });
+    it('check if all services have a network set and all network names start with "hedera-"', () => {
+      const relativePath = '../../..';
+      const files = readdirSync(join(__dirname, relativePath)).filter(name => /^docker-compose(?!.*(evm|multinode)\.yml$).*\.yml$/.test(name));
+      for (const file of files) {
+        const data = readFileSync(join(__dirname, `${relativePath}/${file}`));
+        const config = load(data.toString());
+        const services = config.services || {};
+        for (const [serviceName, serviceConfig] of Object.entries(services)) {
+          if (serviceConfig.extends || (serviceConfig.network_mode || '' === 'none')) {
+            continue; // The child service might have inherited the network. There is no network in non-network mode.
+          }
+          const networks = serviceConfig.networks;
+          expect(networks, `Service '${serviceName}' does not have a network set.`).to.exist;
+        }
+      }
+    });
+  });
+});