diff --git a/end2end-tests/Cargo.toml b/end2end-tests/Cargo.toml
index 9a8647fe6bdebc46538ee2b5610f0309b7a87ccd..07df18c1aa1878428fd2c5c9b1b655df405ea810 100644
--- a/end2end-tests/Cargo.toml
+++ b/end2end-tests/Cargo.toml
@@ -16,6 +16,7 @@ ctrlc = "3.2.2"
 cucumber = "0.11"
 env_logger = "0.9.0"
 hex = "0.4"
+notify = "4.0"
 parity-scale-codec = "3.1.5"
 portpicker = "0.1.1"
 serde_json = "1.0.64"
diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs
index 4b3fd9b662d67f3d1fccde6bce07fd8f8d492da0..24a2658321cf9eea4c6bd42aff42be6e80e90114 100644
--- a/end2end-tests/tests/common/mod.rs
+++ b/end2end-tests/tests/common/mod.rs
@@ -220,18 +220,52 @@ fn spawn_full_node(
 }
 
 fn wait_until_log_line(expected_log_line: &str, log_file_path: &str, timeout: Duration) {
-    let start = Instant::now();
-    loop {
-        let now = Instant::now();
-        if now.duration_since(start) > timeout {
-            eprintln!("Timeout starting node");
-            std::process::exit(1);
+    if cfg!(target_os = "macos") {
+        // MacOs seems to not be able to use inotify (buggy)
+        // So we use a specific implementation for `wait_until_log_line()` here
+        let start = Instant::now();
+        loop {
+            let now = Instant::now();
+            if now.duration_since(start) > timeout {
+                eprintln!("Timeout starting node");
+                std::process::exit(1);
+            }
+            if has_log_line(log_file_path, expected_log_line) {
+                // Ready
+                return;
+            }
+            std::thread::sleep(Duration::from_millis(100));
         }
-        if has_log_line(log_file_path, expected_log_line) {
-            // Ready
-            return;
+    } else {
+        let (tx, rx) = std::sync::mpsc::channel();
+        let mut watcher = notify::watcher(tx, std::time::Duration::from_millis(100)).unwrap();
+        use notify::Watcher as _;
+        watcher
+            .watch(log_file_path, notify::RecursiveMode::NonRecursive)
+            .unwrap();
+
+        let mut pos = 0;
+        loop {
+            match rx.recv_timeout(timeout) {
+                Ok(notify::DebouncedEvent::Write(_)) => {
+                    let mut file = std::fs::File::open(log_file_path).unwrap();
+                    file.seek(std::io::SeekFrom::Start(pos)).unwrap();
+                    pos = file.metadata().unwrap().len();
+                    let reader = std::io::BufReader::new(file);
+
+                    for line in reader.lines() {
+                        if line.expect("fail to read line").contains(expected_log_line) {
+                            return;
+                        }
+                    }
+                }
+                Ok(_) => {}
+                Err(err) => {
+                    eprintln!("Error: {:?}", err);
+                    std::process::exit(1);
+                }
+            }
         }
-        std::thread::sleep(Duration::from_millis(100));
     }
 }