diff --git a/crates/bin/src/args.rs b/crates/bin/src/args.rs
index 27ef49b0..f5a21497 100644
--- a/crates/bin/src/args.rs
+++ b/crates/bin/src/args.rs
@@ -144,6 +144,19 @@ pub struct Args {
     #[clap(help_heading = "Options", long)]
     pub install_path: Option<PathBuf>,
 
+    /// Install binaries with a custom cargo root.
+    ///
+    /// By default, we use `$CARGO_INSTALL_ROOT` or `$CARGO_HOME` as the
+    /// cargo root and global metadata files are updated with the
+    /// package information.
+    ///
+    /// Specifying another path here would install the binaries and update
+    /// the metadata files inside the path you specified.
+    ///
+    /// NOTE that `--install-path` takes precedence over this option.
+    #[clap(help_heading = "Options", long)]
+    pub roots: Option<PathBuf>,
+
     /// Deprecated, here for back-compat only. Secure is now on by default.
     #[clap(hide(true), long)]
     pub secure: bool,
diff --git a/crates/bin/src/entry.rs b/crates/bin/src/entry.rs
index e878bfeb..10fcee1f 100644
--- a/crates/bin/src/entry.rs
+++ b/crates/bin/src/entry.rs
@@ -41,10 +41,17 @@ pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClien
     // Initialize UI thread
     let mut uithread = UIThread::new(!args.no_confirm);
 
-    let (install_path, metadata, temp_dir) = block_in_place(|| -> Result<_> {
+    let (install_path, cargo_roots, metadata, temp_dir) = block_in_place(|| -> Result<_> {
+        // Compute cargo_roots
+        let cargo_roots =
+            install_path::get_cargo_roots_path(args.roots.take()).ok_or_else(|| {
+                error!("No viable cargo roots path found of specified, try `--roots`");
+                miette!("No cargo roots path found or specified")
+            })?;
+
         // Compute install directory
         let (install_path, custom_install_path) =
-            install_path::get_install_path(args.install_path.as_deref());
+            install_path::get_install_path(args.install_path.as_deref(), Some(&cargo_roots));
         let install_path = install_path.ok_or_else(|| {
             error!("No viable install path found of specified, try `--install-path`");
             miette!("No install path found or specified")
@@ -54,8 +61,12 @@ pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClien
 
         // Load metadata
         let metadata = if !custom_install_path {
-            debug!("Reading binstall/crates-v1.json");
-            Some(Records::load()?)
+            let metadata_dir = cargo_roots.join("binstall");
+            fs::create_dir_all(&metadata_dir).map_err(BinstallError::Io)?;
+            let manifest_path = metadata_dir.join("crates-v1.json");
+
+            debug!("Reading {}", manifest_path.display());
+            Some(Records::load_from_path(&manifest_path)?)
         } else {
             None
         };
@@ -71,7 +82,7 @@ pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClien
             .map_err(BinstallError::from)
             .wrap_err("Creating a temporary directory failed.")?;
 
-        Ok((install_path, metadata, temp_dir))
+        Ok((install_path, cargo_roots, metadata, temp_dir))
     })?;
 
     // Remove installed crates
@@ -206,12 +217,11 @@ pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClien
 
     block_in_place(|| {
         if let Some(mut records) = metadata {
-            // If using standardised install path,
-            // then create_dir_all(&install_path) would also
-            // create .cargo.
+            // The cargo manifest path is already created when loading
+            // metadata.
 
             debug!("Writing .crates.toml");
-            CratesToml::append(metadata_vec.iter())?;
+            CratesToml::append_to_path(cargo_roots.join(".crates.toml"), metadata_vec.iter())?;
 
             debug!("Writing binstall/crates-v1.json");
             for metadata in metadata_vec {
diff --git a/crates/bin/src/install_path.rs b/crates/bin/src/install_path.rs
index a979ff77..50a3849e 100644
--- a/crates/bin/src/install_path.rs
+++ b/crates/bin/src/install_path.rs
@@ -1,31 +1,48 @@
 use std::{
+    env::var_os,
     path::{Path, PathBuf},
     sync::Arc,
 };
 
-use binstalk::helpers::statics::cargo_home;
+use binstalk::home::cargo_home;
 use log::debug;
 
+pub fn get_cargo_roots_path(cargo_roots: Option<PathBuf>) -> Option<PathBuf> {
+    if let Some(p) = cargo_roots {
+        return Some(p);
+    }
+
+    // Environmental variables
+    if let Some(p) = var_os("CARGO_INSTALL_ROOT") {
+        let p = PathBuf::from(p);
+        debug!("using CARGO_INSTALL_ROOT ({})", p.display());
+        return Some(p);
+    }
+
+    if let Ok(p) = cargo_home() {
+        debug!("using ({}) as cargo home", p.display());
+        Some(p)
+    } else {
+        None
+    }
+}
+
 /// Fetch install path from environment
 /// roughly follows <https://doc.rust-lang.org/cargo/commands/cargo-install.html#description>
 ///
 /// Return (install_path, is_custom_install_path)
-pub fn get_install_path<P: AsRef<Path>>(install_path: Option<P>) -> (Option<Arc<Path>>, bool) {
+pub fn get_install_path<P: AsRef<Path>>(
+    install_path: Option<P>,
+    cargo_roots: Option<P>,
+) -> (Option<Arc<Path>>, bool) {
     // Command line override first first
     if let Some(p) = install_path {
         return (Some(Arc::from(p.as_ref())), true);
     }
 
-    // Environmental variables
-    if let Ok(p) = std::env::var("CARGO_INSTALL_ROOT") {
-        debug!("using CARGO_INSTALL_ROOT ({p})");
-        let b = PathBuf::from(p);
-        return (Some(Arc::from(b.join("bin"))), true);
-    }
-
-    if let Ok(p) = cargo_home() {
-        debug!("using ({}) as cargo home", p.display());
-        return (Some(p.join("bin").into()), false);
+    // Then cargo_roots
+    if let Some(p) = cargo_roots {
+        return (Some(Arc::from(p.as_ref().join("bin"))), false);
     }
 
     // Local executable dir if no cargo is found
diff --git a/crates/binstalk/src/helpers/statics.rs b/crates/binstalk/src/helpers/statics.rs
index d2ff70ad..7d48aca1 100644
--- a/crates/binstalk/src/helpers/statics.rs
+++ b/crates/binstalk/src/helpers/statics.rs
@@ -1,20 +1,6 @@
-use std::{
-    io::Error,
-    ops::Deref,
-    path::{Path, PathBuf},
-};
-
-use once_cell::sync::{Lazy, OnceCell};
+use once_cell::sync::Lazy;
 use url::Url;
 
-pub fn cargo_home() -> Result<&'static Path, Error> {
-    static CARGO_HOME: OnceCell<PathBuf> = OnceCell::new();
-
-    CARGO_HOME
-        .get_or_try_init(home::cargo_home)
-        .map(Deref::deref)
-}
-
 pub fn cratesio_url() -> &'static Url {
     static CRATESIO: Lazy<Url, fn() -> Url> =
         Lazy::new(|| Url::parse("https://github.com/rust-lang/crates.io-index").unwrap());
diff --git a/crates/binstalk/src/lib.rs b/crates/binstalk/src/lib.rs
index 273f4d97..a4b00d85 100644
--- a/crates/binstalk/src/lib.rs
+++ b/crates/binstalk/src/lib.rs
@@ -8,3 +8,4 @@ pub mod manifests;
 pub mod ops;
 
 pub use detect_targets::{get_desired_targets, DesiredTargets};
+pub use home;
diff --git a/crates/binstalk/src/manifests/binstall_crates_v1.rs b/crates/binstalk/src/manifests/binstall_crates_v1.rs
index c343bc23..69b3df80 100644
--- a/crates/binstalk/src/manifests/binstall_crates_v1.rs
+++ b/crates/binstalk/src/manifests/binstall_crates_v1.rs
@@ -15,11 +15,12 @@ use std::{
 };
 
 use fs_lock::FileLock;
+use home::cargo_home;
 use miette::Diagnostic;
 use serde::Serialize;
 use thiserror::Error;
 
-use crate::{fs::create_if_not_exist, helpers::statics::cargo_home};
+use crate::fs::create_if_not_exist;
 
 use super::crate_info::CrateInfo;
 
diff --git a/crates/binstalk/src/manifests/cargo_crates_v1.rs b/crates/binstalk/src/manifests/cargo_crates_v1.rs
index 5b2ca110..aa1fa2a5 100644
--- a/crates/binstalk/src/manifests/cargo_crates_v1.rs
+++ b/crates/binstalk/src/manifests/cargo_crates_v1.rs
@@ -17,11 +17,12 @@ use std::{
 
 use compact_str::CompactString;
 use fs_lock::FileLock;
+use home::cargo_home;
 use miette::Diagnostic;
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
 
-use crate::{fs::create_if_not_exist, helpers::statics::cargo_home};
+use crate::fs::create_if_not_exist;
 
 use super::crate_info::CrateInfo;