diff --git a/Cargo.lock b/Cargo.lock
index a8e7d7d4cdda1c8d567c772f420678a12d837556..a824faedd6b37e18261c003304cbc5b269b3ed95 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -15992,7 +15992,6 @@ dependencies = [
  "sc-transaction-pool-api",
  "serde",
  "serde_json",
- "serial_test",
  "sp-api 26.0.0",
  "sp-authority-discovery",
  "sp-block-builder",
@@ -20641,31 +20640,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "serial_test"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d"
-dependencies = [
- "dashmap",
- "futures",
- "lazy_static",
- "log",
- "parking_lot 0.12.3",
- "serial_test_derive",
-]
-
-[[package]]
-name = "serial_test_derive"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f"
-dependencies = [
- "proc-macro2 1.0.86",
- "quote 1.0.37",
- "syn 2.0.87",
-]
-
 [[package]]
 name = "sha-1"
 version = "0.9.8"
diff --git a/Cargo.toml b/Cargo.toml
index edfea7b8efa91eaeba04d3128a988020f30c4648..b12469987ed6be459f5e454e3e374c93e4305a9f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1210,7 +1210,6 @@ serde-big-array = { version = "0.3.2" }
 serde_derive = { version = "1.0.117" }
 serde_json = { version = "1.0.132", default-features = false }
 serde_yaml = { version = "0.9" }
-serial_test = { version = "2.0.0" }
 sha1 = { version = "0.10.6" }
 sha2 = { version = "0.10.7", default-features = false }
 sha3 = { version = "0.10.0", default-features = false }
diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml
index 3edb3f4dadbed8ac45ebd71814fb552a93227203..6e8eade21a4389ea1815f2e6463722b7ba896823 100644
--- a/polkadot/node/service/Cargo.toml
+++ b/polkadot/node/service/Cargo.toml
@@ -140,7 +140,6 @@ polkadot-node-subsystem-test-helpers = { workspace = true }
 polkadot-primitives-test-helpers = { workspace = true }
 sp-tracing = { workspace = true }
 assert_matches = { workspace = true }
-serial_test = { workspace = true }
 tempfile = { workspace = true }
 
 [features]
diff --git a/polkadot/node/service/src/workers.rs b/polkadot/node/service/src/workers.rs
index b35bb8302fdc4405f542fd0cc119066653c76cc9..73c3aa466608f108e3fc245e91dcfcf800692813 100644
--- a/polkadot/node/service/src/workers.rs
+++ b/polkadot/node/service/src/workers.rs
@@ -21,19 +21,20 @@ use is_executable::IsExecutable;
 use std::path::PathBuf;
 
 #[cfg(test)]
-use std::sync::{Mutex, OnceLock};
+thread_local! {
+	static TMP_DIR: std::cell::RefCell<Option<tempfile::TempDir>> = std::cell::RefCell::new(None);
+}
 
 /// Override the workers polkadot binary directory path, used for testing.
 #[cfg(test)]
-fn workers_exe_path_override() -> &'static Mutex<Option<PathBuf>> {
-	static OVERRIDE: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
-	OVERRIDE.get_or_init(|| Mutex::new(None))
+fn workers_exe_path_override() -> Option<PathBuf> {
+	TMP_DIR.with_borrow(|t| t.as_ref().map(|t| t.path().join("usr/bin")))
 }
+
 /// Override the workers lib directory path, used for testing.
 #[cfg(test)]
-fn workers_lib_path_override() -> &'static Mutex<Option<PathBuf>> {
-	static OVERRIDE: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
-	OVERRIDE.get_or_init(|| Mutex::new(None))
+fn workers_lib_path_override() -> Option<PathBuf> {
+	TMP_DIR.with_borrow(|t| t.as_ref().map(|t| t.path().join("usr/lib/polkadot")))
 }
 
 /// Determines the final set of paths to use for the PVF workers.
@@ -147,12 +148,9 @@ fn list_workers_paths(
 
 	// Consider the /usr/lib/polkadot/ directory.
 	{
-		#[allow(unused_mut)]
-		let mut lib_path = PathBuf::from("/usr/lib/polkadot");
+		let lib_path = PathBuf::from("/usr/lib/polkadot");
 		#[cfg(test)]
-		if let Some(ref path_override) = *workers_lib_path_override().lock().unwrap() {
-			lib_path = path_override.clone();
-		}
+		let lib_path = if let Some(o) = workers_lib_path_override() { o } else { lib_path };
 
 		let (prep_worker, exec_worker) = build_worker_paths(lib_path, workers_names);
 
@@ -175,9 +173,10 @@ fn get_exe_path() -> Result<PathBuf, Error> {
 	let mut exe_path = std::env::current_exe()?;
 	let _ = exe_path.pop(); // executable file will always have a parent directory.
 	#[cfg(test)]
-	if let Some(ref path_override) = *workers_exe_path_override().lock().unwrap() {
-		exe_path = path_override.clone();
+	if let Some(o) = workers_exe_path_override() {
+		exe_path = o;
 	}
+
 	Ok(exe_path)
 }
 
@@ -205,8 +204,7 @@ mod tests {
 	use super::*;
 
 	use assert_matches::assert_matches;
-	use serial_test::serial;
-	use std::{env::temp_dir, fs, os::unix::fs::PermissionsExt, path::Path};
+	use std::{fs, os::unix::fs::PermissionsExt, path::Path};
 
 	const TEST_NODE_VERSION: &'static str = "v0.1.2";
 
@@ -228,7 +226,7 @@ mod tests {
 
 	fn get_program(version: &str) -> String {
 		format!(
-			"#!/bin/bash
+			"#!/usr/bin/env bash
 
 if [[ $# -ne 1 ]] ; then
     echo \"unexpected number of arguments: $#\"
@@ -253,27 +251,21 @@ echo {}
 	) -> Result<(), Box<dyn std::error::Error>> {
 		// Set up <tmp>/usr/lib/polkadot and <tmp>/usr/bin, both empty.
 
-		let tempdir = temp_dir();
-		let lib_path = tempdir.join("usr/lib/polkadot");
-		let _ = fs::remove_dir_all(&lib_path);
-		fs::create_dir_all(&lib_path)?;
-		*workers_lib_path_override().lock()? = Some(lib_path);
+		let tempdir = tempfile::tempdir().unwrap();
+		let tmp_dir = tempdir.path().to_path_buf();
+		TMP_DIR.with_borrow_mut(|t| *t = Some(tempdir));
 
-		let exe_path = tempdir.join("usr/bin");
-		let _ = fs::remove_dir_all(&exe_path);
-		fs::create_dir_all(&exe_path)?;
-		*workers_exe_path_override().lock()? = Some(exe_path.clone());
+		fs::create_dir_all(workers_lib_path_override().unwrap()).unwrap();
+		fs::create_dir_all(workers_exe_path_override().unwrap()).unwrap();
 
+		let custom_path = tmp_dir.join("usr/local/bin");
 		// Set up custom path at <tmp>/usr/local/bin.
-		let custom_path = tempdir.join("usr/local/bin");
-		let _ = fs::remove_dir_all(&custom_path);
-		fs::create_dir_all(&custom_path)?;
+		fs::create_dir_all(&custom_path).unwrap();
 
-		f(tempdir, exe_path)
+		f(tmp_dir, workers_exe_path_override().unwrap())
 	}
 
 	#[test]
-	#[serial]
 	fn test_given_worker_path() {
 		with_temp_dir_structure(|tempdir, exe_path| {
 			let given_workers_path = tempdir.join("usr/local/bin");
@@ -318,7 +310,6 @@ echo {}
 	}
 
 	#[test]
-	#[serial]
 	fn missing_workers_paths_throws_error() {
 		with_temp_dir_structure(|tempdir, exe_path| {
 			// Try with both binaries missing.
@@ -368,7 +359,6 @@ echo {}
 	}
 
 	#[test]
-	#[serial]
 	fn should_find_workers_at_all_locations() {
 		with_temp_dir_structure(|tempdir, _| {
 				let prepare_worker_bin_path = tempdir.join("usr/bin/polkadot-prepare-worker");
@@ -394,7 +384,6 @@ echo {}
 	}
 
 	#[test]
-	#[serial]
 	fn should_find_workers_with_custom_names_at_all_locations() {
 		with_temp_dir_structure(|tempdir, _| {
 			let (prep_worker_name, exec_worker_name) = ("test-prepare", "test-execute");
@@ -422,7 +411,6 @@ echo {}
 	}
 
 	#[test]
-	#[serial]
 	fn workers_version_mismatch_throws_error() {
 		let bad_version = "v9.9.9.9";
 
@@ -474,7 +462,6 @@ echo {}
 	}
 
 	#[test]
-	#[serial]
 	fn should_find_valid_workers() {
 		// Test bin location.
 		with_temp_dir_structure(|tempdir, _| {