diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml
index f6d563b64d..67c59a4b48 100644
--- a/ext/fs/Cargo.toml
+++ b/ext/fs/Cargo.toml
@@ -13,6 +13,9 @@ description = "Ops for interacting with the file system"
 [lib]
 path = "lib.rs"
 
+[features]
+sync_fs = []
+
 [dependencies]
 async-trait.workspace = true
 deno_core.workspace = true
diff --git a/ext/fs/clippy.toml b/ext/fs/clippy.toml
index 53676a90e6..023769214b 100644
--- a/ext/fs/clippy.toml
+++ b/ext/fs/clippy.toml
@@ -43,3 +43,6 @@ disallowed-methods = [
   { path = "std::path::Path::canonicalize", reason = "File system operations should be done using FileSystem trait" },
   { path = "std::path::Path::exists", reason = "File system operations should be done using FileSystem trait" },
 ]
+disallowed-types = [
+  { path = "std::sync::Arc", reason = "use crate::sync::MaybeArc instead" },
+]
diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs
index 474089153e..2d9b68f55d 100644
--- a/ext/fs/interface.rs
+++ b/ext/fs/interface.rs
@@ -11,6 +11,9 @@ use deno_io::fs::File;
 use deno_io::fs::FsResult;
 use deno_io::fs::FsStat;
 
+use crate::sync::MaybeSend;
+use crate::sync::MaybeSync;
+
 #[derive(Deserialize, Default, Debug, Clone, Copy)]
 #[serde(rename_all = "camelCase")]
 #[serde(default)]
@@ -72,8 +75,11 @@ pub struct FsDirEntry {
   pub is_symlink: bool,
 }
 
+#[allow(clippy::disallowed_types)]
+pub type FileSystemRc = crate::sync::MaybeArc<dyn FileSystem>;
+
 #[async_trait::async_trait(?Send)]
-pub trait FileSystem: std::fmt::Debug + Send + Sync {
+pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
   fn cwd(&self) -> FsResult<PathBuf>;
   fn tmp_dir(&self) -> FsResult<PathBuf>;
   fn chdir(&self, path: &Path) -> FsResult<()>;
diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs
index 4fdf6b3f11..fb0a6ffedb 100644
--- a/ext/fs/lib.rs
+++ b/ext/fs/lib.rs
@@ -3,14 +3,18 @@
 mod interface;
 mod ops;
 mod std_fs;
+pub mod sync;
 
 pub use crate::interface::FileSystem;
+pub use crate::interface::FileSystemRc;
 pub use crate::interface::FsDirEntry;
 pub use crate::interface::FsFileType;
 pub use crate::interface::OpenOptions;
-use crate::ops::*;
-
 pub use crate::std_fs::RealFs;
+pub use crate::sync::MaybeSend;
+pub use crate::sync::MaybeSync;
+
+use crate::ops::*;
 
 use deno_core::error::AnyError;
 use deno_core::OpState;
@@ -18,7 +22,6 @@ use std::cell::RefCell;
 use std::convert::From;
 use std::path::Path;
 use std::rc::Rc;
-use std::sync::Arc;
 
 pub trait FsPermissions {
   fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError>;
@@ -153,7 +156,7 @@ deno_core::extension!(deno_fs,
   esm = [ "30_fs.js" ],
   options = {
     unstable: bool,
-    fs: Arc<dyn FileSystem>,
+    fs: FileSystemRc,
   },
   state = |state, options| {
     state.put(UnstableChecker { unstable: options.unstable });
diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs
index c9996d8ce7..b866f86458 100644
--- a/ext/fs/ops.rs
+++ b/ext/fs/ops.rs
@@ -7,7 +7,6 @@ use std::io::SeekFrom;
 use std::path::Path;
 use std::path::PathBuf;
 use std::rc::Rc;
-use std::sync::Arc;
 
 use deno_core::error::custom_error;
 use deno_core::error::type_error;
@@ -28,9 +27,9 @@ use serde::Serialize;
 
 use crate::check_unstable;
 use crate::check_unstable2;
+use crate::interface::FileSystemRc;
 use crate::interface::FsDirEntry;
 use crate::interface::FsFileType;
-use crate::FileSystem;
 use crate::FsPermissions;
 use crate::OpenOptions;
 
@@ -39,7 +38,7 @@ pub fn op_cwd<P>(state: &mut OpState) -> Result<String, AnyError>
 where
   P: FsPermissions + 'static,
 {
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let path = fs.cwd()?;
   state
     .borrow_mut::<P>()
@@ -56,7 +55,7 @@ where
   let d = PathBuf::from(&directory);
   state.borrow_mut::<P>().check_read(&d, "Deno.chdir()")?;
   state
-    .borrow::<Arc<dyn FileSystem>>()
+    .borrow::<FileSystemRc>()
     .chdir(&d)
     .context_path("chdir", &d)
 }
@@ -66,10 +65,7 @@ fn op_umask(state: &mut OpState, mask: Option<u32>) -> Result<u32, AnyError>
 where
 {
   check_unstable(state, "Deno.umask");
-  state
-    .borrow::<Arc<dyn FileSystem>>()
-    .umask(mask)
-    .context("umask")
+  state.borrow::<FileSystemRc>().umask(mask).context("umask")
 }
 
 #[op]
@@ -87,7 +83,7 @@ where
   let permissions = state.borrow_mut::<P>();
   permissions.check(&options, &path, "Deno.openSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let file = fs.open_sync(&path, options).context_path("open", &path)?;
 
   let rid = state
@@ -112,7 +108,7 @@ where
     let mut state = state.borrow_mut();
     let permissions = state.borrow_mut::<P>();
     permissions.check(&options, &path, "Deno.open()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
   let file = fs
     .open_async(path.clone(), options)
@@ -144,7 +140,7 @@ where
     .borrow_mut::<P>()
     .check_write(&path, "Deno.mkdirSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.mkdir_sync(&path, recursive, mode)
     .context_path("mkdir", &path)?;
 
@@ -168,7 +164,7 @@ where
   let fs = {
     let mut state = state.borrow_mut();
     state.borrow_mut::<P>().check_write(&path, "Deno.mkdir()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.mkdir_async(path.clone(), recursive, mode)
@@ -191,7 +187,7 @@ where
   state
     .borrow_mut::<P>()
     .check_write(&path, "Deno.chmodSync()")?;
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.chmod_sync(&path, mode).context_path("chmod", &path)?;
   Ok(())
 }
@@ -209,7 +205,7 @@ where
   let fs = {
     let mut state = state.borrow_mut();
     state.borrow_mut::<P>().check_write(&path, "Deno.chmod()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
   fs.chmod_async(path.clone(), mode)
     .await
@@ -231,7 +227,7 @@ where
   state
     .borrow_mut::<P>()
     .check_write(&path, "Deno.chownSync()")?;
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.chown_sync(&path, uid, gid)
     .context_path("chown", &path)?;
   Ok(())
@@ -251,7 +247,7 @@ where
   let fs = {
     let mut state = state.borrow_mut();
     state.borrow_mut::<P>().check_write(&path, "Deno.chown()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
   fs.chown_async(path.clone(), uid, gid)
     .await
@@ -274,7 +270,7 @@ where
     .borrow_mut::<P>()
     .check_write(&path, "Deno.removeSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.remove_sync(&path, recursive)
     .context_path("remove", &path)?;
 
@@ -297,7 +293,7 @@ where
     state
       .borrow_mut::<P>()
       .check_write(&path, "Deno.remove()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.remove_async(path.clone(), recursive)
@@ -323,7 +319,7 @@ where
   permissions.check_read(&from, "Deno.copyFileSync()")?;
   permissions.check_write(&to, "Deno.copyFileSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.copy_file_sync(&from, &to)
     .context_two_path("copy", &from, &to)?;
 
@@ -347,7 +343,7 @@ where
     let permissions = state.borrow_mut::<P>();
     permissions.check_read(&from, "Deno.copyFile()")?;
     permissions.check_write(&to, "Deno.copyFile()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.copy_file_async(from.clone(), to.clone())
@@ -370,7 +366,7 @@ where
   state
     .borrow_mut::<P>()
     .check_read(&path, "Deno.statSync()")?;
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let stat = fs.stat_sync(&path).context_path("stat", &path)?;
   let serializable_stat = SerializableStat::from(stat);
   serializable_stat.write(stat_out_buf);
@@ -390,7 +386,7 @@ where
     let mut state = state.borrow_mut();
     let permissions = state.borrow_mut::<P>();
     permissions.check_read(&path, "Deno.stat()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
   let stat = fs
     .stat_async(path.clone())
@@ -412,7 +408,7 @@ where
   state
     .borrow_mut::<P>()
     .check_read(&path, "Deno.lstatSync()")?;
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let stat = fs.lstat_sync(&path).context_path("lstat", &path)?;
   let serializable_stat = SerializableStat::from(stat);
   serializable_stat.write(stat_out_buf);
@@ -432,7 +428,7 @@ where
     let mut state = state.borrow_mut();
     let permissions = state.borrow_mut::<P>();
     permissions.check_read(&path, "Deno.lstat()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
   let stat = fs
     .lstat_async(path.clone())
@@ -451,7 +447,7 @@ where
 {
   let path = PathBuf::from(path);
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>().clone();
+  let fs = state.borrow::<FileSystemRc>().clone();
   let permissions = state.borrow_mut::<P>();
   permissions.check_read(&path, "Deno.realPathSync()")?;
   if path.is_relative() {
@@ -478,7 +474,7 @@ where
   let fs;
   {
     let mut state = state.borrow_mut();
-    fs = state.borrow::<Arc<dyn FileSystem>>().clone();
+    fs = state.borrow::<FileSystemRc>().clone();
     let permissions = state.borrow_mut::<P>();
     permissions.check_read(&path, "Deno.realPath()")?;
     if path.is_relative() {
@@ -508,7 +504,7 @@ where
     .borrow_mut::<P>()
     .check_read(&path, "Deno.readDirSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let entries = fs.read_dir_sync(&path).context_path("readdir", &path)?;
 
   Ok(entries)
@@ -529,7 +525,7 @@ where
     state
       .borrow_mut::<P>()
       .check_read(&path, "Deno.readDir()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   let entries = fs
@@ -557,7 +553,7 @@ where
   permissions.check_write(&oldpath, "Deno.renameSync()")?;
   permissions.check_write(&newpath, "Deno.renameSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.rename_sync(&oldpath, &newpath)
     .context_two_path("rename", &oldpath, &newpath)?;
 
@@ -582,7 +578,7 @@ where
     permissions.check_read(&oldpath, "Deno.rename()")?;
     permissions.check_write(&oldpath, "Deno.rename()")?;
     permissions.check_write(&newpath, "Deno.rename()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.rename_async(oldpath.clone(), newpath.clone())
@@ -610,7 +606,7 @@ where
   permissions.check_read(&newpath, "Deno.linkSync()")?;
   permissions.check_write(&newpath, "Deno.linkSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.link_sync(&oldpath, &newpath)
     .context_two_path("link", &oldpath, &newpath)?;
 
@@ -636,7 +632,7 @@ where
     permissions.check_write(&oldpath, "Deno.link()")?;
     permissions.check_read(&newpath, "Deno.link()")?;
     permissions.check_write(&newpath, "Deno.link()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.link_async(oldpath.clone(), newpath.clone())
@@ -663,7 +659,7 @@ where
   permissions.check_write_all("Deno.symlinkSync()")?;
   permissions.check_read_all("Deno.symlinkSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.symlink_sync(&oldpath, &newpath, file_type)
     .context_two_path("symlink", &oldpath, &newpath)?;
 
@@ -688,7 +684,7 @@ where
     let permissions = state.borrow_mut::<P>();
     permissions.check_write_all("Deno.symlink()")?;
     permissions.check_read_all("Deno.symlink()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.symlink_async(oldpath.clone(), newpath.clone(), file_type)
@@ -712,7 +708,7 @@ where
     .borrow_mut::<P>()
     .check_read(&path, "Deno.readLink()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
 
   let target = fs.read_link_sync(&path).context_path("readlink", &path)?;
   let target_string = path_into_string(target.into_os_string())?;
@@ -734,7 +730,7 @@ where
     state
       .borrow_mut::<P>()
       .check_read(&path, "Deno.readLink()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   let target = fs
@@ -760,7 +756,7 @@ where
     .borrow_mut::<P>()
     .check_write(&path, "Deno.truncateSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.truncate_sync(&path, len)
     .context_path("truncate", &path)?;
 
@@ -783,7 +779,7 @@ where
     state
       .borrow_mut::<P>()
       .check_write(&path, "Deno.truncate()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.truncate_async(path.clone(), len)
@@ -809,7 +805,7 @@ where
 
   state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)
     .context_path("utime", &path)?;
 
@@ -833,7 +829,7 @@ where
   let fs = {
     let mut state = state.borrow_mut();
     state.borrow_mut::<P>().check_write(&path, "Deno.utime()")?;
-    state.borrow::<Arc<dyn FileSystem>>().clone()
+    state.borrow::<FileSystemRc>().clone()
   };
 
   fs.utime_async(
@@ -997,11 +993,11 @@ where
 fn make_temp_check_sync<P>(
   state: &mut OpState,
   dir: Option<String>,
-) -> Result<(PathBuf, Arc<dyn FileSystem>), AnyError>
+) -> Result<(PathBuf, FileSystemRc), AnyError>
 where
   P: FsPermissions + 'static,
 {
-  let fs = state.borrow::<Arc<dyn FileSystem>>().clone();
+  let fs = state.borrow::<FileSystemRc>().clone();
   let dir = match dir {
     Some(dir) => {
       let dir = PathBuf::from(dir);
@@ -1026,12 +1022,12 @@ where
 fn make_temp_check_async<P>(
   state: Rc<RefCell<OpState>>,
   dir: Option<String>,
-) -> Result<(PathBuf, Arc<dyn FileSystem>), AnyError>
+) -> Result<(PathBuf, FileSystemRc), AnyError>
 where
   P: FsPermissions + 'static,
 {
   let mut state = state.borrow_mut();
-  let fs = state.borrow::<Arc<dyn FileSystem>>().clone();
+  let fs = state.borrow::<FileSystemRc>().clone();
   let dir = match dir {
     Some(dir) => {
       let dir = PathBuf::from(dir);
@@ -1089,7 +1085,7 @@ where
   let options = OpenOptions::write(create, append, create_new, mode);
   permissions.check(&options, &path, "Deno.writeFileSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
 
   fs.write_file_sync(&path, options, &data)
     .context_path("writefile", &path)?;
@@ -1121,7 +1117,7 @@ where
     permissions.check(&options, &path, "Deno.writeFile()")?;
     let cancel_handle = cancel_rid
       .and_then(|rid| state.resource_table.get::<CancelHandle>(rid).ok());
-    (state.borrow::<Arc<dyn FileSystem>>().clone(), cancel_handle)
+    (state.borrow::<FileSystemRc>().clone(), cancel_handle)
   };
 
   let fut = fs.write_file_async(path.clone(), options, data.to_vec());
@@ -1154,7 +1150,7 @@ where
   let permissions = state.borrow_mut::<P>();
   permissions.check_read(&path, "Deno.readFileSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let buf = fs.read_file_sync(&path).context("readfile")?;
 
   Ok(buf.into())
@@ -1177,7 +1173,7 @@ where
     permissions.check_read(&path, "Deno.readFile()")?;
     let cancel_handle = cancel_rid
       .and_then(|rid| state.resource_table.get::<CancelHandle>(rid).ok());
-    (state.borrow::<Arc<dyn FileSystem>>().clone(), cancel_handle)
+    (state.borrow::<FileSystemRc>().clone(), cancel_handle)
   };
 
   let fut = fs.read_file_async(path.clone());
@@ -1210,7 +1206,7 @@ where
   let permissions = state.borrow_mut::<P>();
   permissions.check_read(&path, "Deno.readFileSync()")?;
 
-  let fs = state.borrow::<Arc<dyn FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let buf = fs.read_file_sync(&path).context("readfile")?;
 
   Ok(string_from_utf8_lossy(buf))
@@ -1233,7 +1229,7 @@ where
     permissions.check_read(&path, "Deno.readFile()")?;
     let cancel_handle = cancel_rid
       .and_then(|rid| state.resource_table.get::<CancelHandle>(rid).ok());
-    (state.borrow::<Arc<dyn FileSystem>>().clone(), cancel_handle)
+    (state.borrow::<FileSystemRc>().clone(), cancel_handle)
   };
 
   let fut = fs.read_file_async(path.clone());
diff --git a/ext/fs/sync.rs b/ext/fs/sync.rs
new file mode 100644
index 0000000000..c43850c287
--- /dev/null
+++ b/ext/fs/sync.rs
@@ -0,0 +1,22 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+pub use inner::*;
+
+#[cfg(feature = "sync_fs")]
+mod inner {
+  #![allow(clippy::disallowed_types)]
+  pub use std::sync::Arc as MaybeArc;
+
+  pub use core::marker::Send as MaybeSend;
+  pub use core::marker::Sync as MaybeSync;
+}
+
+#[cfg(not(feature = "sync_fs"))]
+mod inner {
+  pub use std::rc::Rc as MaybeArc;
+
+  pub trait MaybeSync {}
+  impl<T> MaybeSync for T where T: ?Sized {}
+  pub trait MaybeSend {}
+  impl<T> MaybeSend for T where T: ?Sized {}
+}
diff --git a/ext/node/analyze.rs b/ext/node/analyze.rs
index bad0906c5b..6d32c68beb 100644
--- a/ext/node/analyze.rs
+++ b/ext/node/analyze.rs
@@ -5,7 +5,6 @@ use std::collections::VecDeque;
 use std::fmt::Write;
 use std::path::Path;
 use std::path::PathBuf;
-use std::sync::Arc;
 
 use deno_core::anyhow::Context;
 use deno_core::ModuleSpecifier;
@@ -13,11 +12,11 @@ use once_cell::sync::Lazy;
 
 use deno_core::error::AnyError;
 
+use crate::resolution::NodeResolverRc;
 use crate::NodeModuleKind;
 use crate::NodePermissions;
 use crate::NodeResolutionMode;
-use crate::NodeResolver;
-use crate::NpmResolver;
+use crate::NpmResolverRc;
 use crate::PackageJson;
 use crate::PathClean;
 use crate::NODE_GLOBAL_THIS_NAME;
@@ -66,9 +65,9 @@ pub trait CjsEsmCodeAnalyzer {
 
 pub struct NodeCodeTranslator<TCjsEsmCodeAnalyzer: CjsEsmCodeAnalyzer> {
   cjs_esm_code_analyzer: TCjsEsmCodeAnalyzer,
-  fs: Arc<dyn deno_fs::FileSystem>,
-  node_resolver: Arc<NodeResolver>,
-  npm_resolver: Arc<dyn NpmResolver>,
+  fs: deno_fs::FileSystemRc,
+  node_resolver: NodeResolverRc,
+  npm_resolver: NpmResolverRc,
 }
 
 impl<TCjsEsmCodeAnalyzer: CjsEsmCodeAnalyzer>
@@ -76,9 +75,9 @@ impl<TCjsEsmCodeAnalyzer: CjsEsmCodeAnalyzer>
 {
   pub fn new(
     cjs_esm_code_analyzer: TCjsEsmCodeAnalyzer,
-    fs: Arc<dyn deno_fs::FileSystem>,
-    node_resolver: Arc<NodeResolver>,
-    npm_resolver: Arc<dyn NpmResolver>,
+    fs: deno_fs::FileSystemRc,
+    node_resolver: NodeResolverRc,
+    npm_resolver: NpmResolverRc,
   ) -> Self {
     Self {
       cjs_esm_code_analyzer,
diff --git a/ext/node/clippy.toml b/ext/node/clippy.toml
index 31d9d7d472..02fd259d09 100644
--- a/ext/node/clippy.toml
+++ b/ext/node/clippy.toml
@@ -38,3 +38,6 @@ disallowed-methods = [
   { path = "std::fs::symlink_metadata", reason = "File system operations should be done using FileSystem trait" },
   { path = "std::fs::write", reason = "File system operations should be done using FileSystem trait" },
 ]
+disallowed-types = [
+  { path = "std::sync::Arc", reason = "use deno_fs::sync::MaybeArc instead" },
+]
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 03ec730d84..e01954109a 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -1,11 +1,18 @@
 // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
 
+use std::collections::HashSet;
+use std::path::Path;
+use std::path::PathBuf;
+use std::rc::Rc;
+
 use deno_core::error::AnyError;
 use deno_core::located_script_name;
 use deno_core::op;
 use deno_core::serde_json;
 use deno_core::JsRuntime;
 use deno_core::ModuleSpecifier;
+use deno_fs::sync::MaybeSend;
+use deno_fs::sync::MaybeSync;
 use deno_npm::resolution::PackageReqNotFoundError;
 use deno_npm::NpmPackageId;
 use deno_semver::npm::NpmPackageNv;
@@ -13,11 +20,6 @@ use deno_semver::npm::NpmPackageNvReference;
 use deno_semver::npm::NpmPackageReq;
 use deno_semver::npm::NpmPackageReqReference;
 use once_cell::sync::Lazy;
-use std::collections::HashSet;
-use std::path::Path;
-use std::path::PathBuf;
-use std::rc::Rc;
-use std::sync::Arc;
 
 pub mod analyze;
 pub mod errors;
@@ -50,7 +52,10 @@ impl NodePermissions for AllowAllNodePermissions {
   }
 }
 
-pub trait NpmResolver: std::fmt::Debug + Send + Sync {
+#[allow(clippy::disallowed_types)]
+pub type NpmResolverRc = deno_fs::sync::MaybeArc<dyn NpmResolver>;
+
+pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
   /// Resolves an npm package folder path from an npm package referrer.
   fn resolve_package_folder_from_package(
     &self,
@@ -449,8 +454,8 @@ deno_core::extension!(deno_node,
     "zlib.ts",
   ],
   options = {
-    maybe_npm_resolver: Option<Arc<dyn NpmResolver>>,
-    fs: Arc<dyn deno_fs::FileSystem>,
+    maybe_npm_resolver: Option<NpmResolverRc>,
+    fs: deno_fs::FileSystemRc,
   },
   state = |state, options| {
     let fs = options.fs;
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index 972815995a..9e13681aed 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -9,18 +9,18 @@ use deno_core::url::Url;
 use deno_core::JsRuntimeInspector;
 use deno_core::ModuleSpecifier;
 use deno_core::OpState;
+use deno_fs::FileSystemRc;
 use std::cell::RefCell;
 use std::path::Path;
 use std::path::PathBuf;
 use std::rc::Rc;
-use std::sync::Arc;
 
 use crate::resolution;
 use crate::NodeModuleKind;
 use crate::NodePermissions;
 use crate::NodeResolutionMode;
 use crate::NodeResolver;
-use crate::NpmResolver;
+use crate::NpmResolverRc;
 use crate::PackageJson;
 
 fn ensure_read_permission<P>(
@@ -30,7 +30,7 @@ fn ensure_read_permission<P>(
 where
   P: NodePermissions + 'static,
 {
-  let resolver = state.borrow::<Arc<dyn NpmResolver>>();
+  let resolver = state.borrow::<NpmResolverRc>();
   let permissions = state.borrow::<P>();
   resolver.ensure_read_permission(permissions, file_path)
 }
@@ -93,7 +93,7 @@ pub fn op_require_node_module_paths<P>(
 where
   P: NodePermissions + 'static,
 {
-  let fs = state.borrow::<Arc<dyn deno_fs::FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   // Guarantee that "from" is absolute.
   let from = deno_core::resolve_path(
     &from,
@@ -189,7 +189,7 @@ fn op_require_resolve_deno_dir(
   request: String,
   parent_filename: String,
 ) -> Option<String> {
-  let resolver = state.borrow::<Arc<dyn NpmResolver>>();
+  let resolver = state.borrow::<NpmResolverRc>();
   resolver
     .resolve_package_folder_from_package(
       &request,
@@ -202,7 +202,7 @@ fn op_require_resolve_deno_dir(
 
 #[op]
 fn op_require_is_deno_dir_package(state: &mut OpState, path: String) -> bool {
-  let resolver = state.borrow::<Arc<dyn NpmResolver>>();
+  let resolver = state.borrow::<NpmResolverRc>();
   resolver.in_npm_package_at_path(&PathBuf::from(path))
 }
 
@@ -262,7 +262,7 @@ where
 {
   let path = PathBuf::from(path);
   ensure_read_permission::<P>(state, &path)?;
-  let fs = state.borrow::<Arc<dyn deno_fs::FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   if let Ok(metadata) = fs.stat_sync(&path) {
     if metadata.is_file {
       return Ok(0);
@@ -284,7 +284,7 @@ where
 {
   let path = PathBuf::from(request);
   ensure_read_permission::<P>(state, &path)?;
-  let fs = state.borrow::<Arc<dyn deno_fs::FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   let canonicalized_path =
     deno_core::strip_unc_prefix(fs.realpath_sync(&path)?);
   Ok(canonicalized_path.to_string_lossy().to_string())
@@ -346,7 +346,7 @@ where
 
   if let Some(parent_id) = maybe_parent_id {
     if parent_id == "<repl>" || parent_id == "internal/preload" {
-      let fs = state.borrow::<Arc<dyn deno_fs::FileSystem>>();
+      let fs = state.borrow::<FileSystemRc>();
       if let Ok(cwd) = fs.cwd() {
         ensure_read_permission::<P>(state, &cwd)?;
         return Ok(Some(cwd.to_string_lossy().to_string()));
@@ -429,7 +429,7 @@ where
 {
   let file_path = PathBuf::from(file_path);
   ensure_read_permission::<P>(state, &file_path)?;
-  let fs = state.borrow::<Arc<dyn deno_fs::FileSystem>>();
+  let fs = state.borrow::<FileSystemRc>();
   Ok(fs.read_to_string(&file_path)?)
 }
 
@@ -457,8 +457,8 @@ fn op_require_resolve_exports<P>(
 where
   P: NodePermissions + 'static,
 {
-  let fs = state.borrow::<Arc<dyn deno_fs::FileSystem>>();
-  let npm_resolver = state.borrow::<Arc<dyn NpmResolver>>();
+  let fs = state.borrow::<FileSystemRc>();
+  let npm_resolver = state.borrow::<NpmResolverRc>();
   let node_resolver = state.borrow::<Rc<NodeResolver>>();
   let permissions = state.borrow::<P>();
 
diff --git a/ext/node/resolution.rs b/ext/node/resolution.rs
index 71b988c194..16720f22c0 100644
--- a/ext/node/resolution.rs
+++ b/ext/node/resolution.rs
@@ -2,7 +2,6 @@
 
 use std::path::Path;
 use std::path::PathBuf;
-use std::sync::Arc;
 
 use deno_core::anyhow::bail;
 use deno_core::anyhow::Context;
@@ -12,6 +11,7 @@ use deno_core::serde_json::Map;
 use deno_core::serde_json::Value;
 use deno_core::url::Url;
 use deno_core::ModuleSpecifier;
+use deno_fs::FileSystemRc;
 use deno_media_type::MediaType;
 use deno_semver::npm::NpmPackageNv;
 use deno_semver::npm::NpmPackageNvReference;
@@ -20,7 +20,7 @@ use deno_semver::npm::NpmPackageReqReference;
 use crate::errors;
 use crate::AllowAllNodePermissions;
 use crate::NodePermissions;
-use crate::NpmResolver;
+use crate::NpmResolverRc;
 use crate::PackageJson;
 use crate::PathClean;
 
@@ -104,17 +104,17 @@ impl NodeResolution {
   }
 }
 
+#[allow(clippy::disallowed_types)]
+pub type NodeResolverRc = deno_fs::sync::MaybeArc<NodeResolver>;
+
 #[derive(Debug)]
 pub struct NodeResolver {
-  fs: Arc<dyn deno_fs::FileSystem>,
-  npm_resolver: Arc<dyn NpmResolver>,
+  fs: FileSystemRc,
+  npm_resolver: NpmResolverRc,
 }
 
 impl NodeResolver {
-  pub fn new(
-    fs: Arc<dyn deno_fs::FileSystem>,
-    npm_resolver: Arc<dyn NpmResolver>,
-  ) -> Self {
+  pub fn new(fs: FileSystemRc, npm_resolver: NpmResolverRc) -> Self {
     Self { fs, npm_resolver }
   }
 
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 8618714dca..9f9c65af1d 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -41,7 +41,7 @@ deno_core.workspace = true
 deno_crypto.workspace = true
 deno_fetch.workspace = true
 deno_ffi.workspace = true
-deno_fs.workspace = true
+deno_fs = { workspace = true, features = ["sync_fs"] }
 deno_http.workspace = true
 deno_io.workspace = true
 deno_net.workspace = true
@@ -67,7 +67,7 @@ deno_core.workspace = true
 deno_crypto.workspace = true
 deno_fetch.workspace = true
 deno_ffi.workspace = true
-deno_fs.workspace = true
+deno_fs = { workspace = true, features = ["sync_fs"] }
 deno_http.workspace = true
 deno_io.workspace = true
 deno_kv.workspace = true