diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 00f8105aea33cf77882e3f05383de95cdd30fc4d..e3ce564b0aba10454ecae3410dd46a21154d7981 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -766,7 +766,7 @@ BuiltPaths build(ref<Store> evalStore, ref<Store> store, Realise mode,
     if (mode == Realise::Nothing)
         printMissing(store, pathsToBuild, lvlError);
     else if (mode == Realise::Outputs)
-        store->buildPaths(pathsToBuild, bMode);
+        store->buildPaths(pathsToBuild, bMode, evalStore);
 
     return getBuiltPaths(evalStore, store, pathsToBuild);
 }
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index 7df59740014e46d15ff65ad2c14a7db2c40bfe0d..dad8717a2c4a670af57598e959146e091ddf3d91 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -165,7 +165,7 @@ void DerivationGoal::getDerivation()
     /* The first thing to do is to make sure that the derivation
        exists.  If it doesn't, it may be created through a
        substitute. */
-    if (buildMode == bmNormal && worker.store.isValidPath(drvPath)) {
+    if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) {
         loadDerivation();
         return;
     }
@@ -188,12 +188,12 @@ void DerivationGoal::loadDerivation()
     /* `drvPath' should already be a root, but let's be on the safe
        side: if the user forgot to make it a root, we wouldn't want
        things being garbage collected while we're busy. */
-    worker.store.addTempRoot(drvPath);
+    worker.evalStore.addTempRoot(drvPath);
 
-    assert(worker.store.isValidPath(drvPath));
+    assert(worker.evalStore.isValidPath(drvPath));
 
     /* Get the derivation. */
-    drv = std::make_unique<Derivation>(worker.store.derivationFromPath(drvPath));
+    drv = std::make_unique<Derivation>(worker.evalStore.derivationFromPath(drvPath));
 
     haveDerivation();
 }
@@ -212,8 +212,8 @@ void DerivationGoal::haveDerivation()
         if (i.second.second)
             worker.store.addTempRoot(*i.second.second);
 
-    auto outputHashes = staticOutputHashes(worker.store, *drv);
-    for (auto &[outputName, outputHash] : outputHashes)
+    auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
+    for (auto & [outputName, outputHash] : outputHashes)
       initialOutputs.insert({
             outputName,
             InitialOutput{
@@ -337,6 +337,16 @@ void DerivationGoal::gaveUpOnSubstitution()
         for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs)
             addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal));
 
+    /* Copy the input sources from the eval store to the build
+       store. */
+    if (&worker.evalStore != &worker.store) {
+        RealisedPath::Set inputSrcs, inputClosure;
+        for (auto & i : drv->inputSrcs)
+            inputSrcs.insert(i);
+        RealisedPath::closure(worker.evalStore, inputSrcs, inputClosure);
+        copyClosure(worker.evalStore, worker.store, inputClosure);
+    }
+
     for (auto & i : drv->inputSrcs) {
         if (worker.store.isValidPath(i)) continue;
         if (!settings.useSubstitutes)
@@ -478,8 +488,8 @@ void DerivationGoal::inputsRealised()
             /* Add the relevant output closures of the input derivation
                `i' as input paths.  Only add the closures of output paths
                that are specified as inputs. */
-            assert(worker.store.isValidPath(drvPath));
-            auto outputs = worker.store.queryPartialDerivationOutputMap(depDrvPath);
+            assert(worker.evalStore.isValidPath(drvPath));
+            auto outputs = worker.evalStore.queryPartialDerivationOutputMap(depDrvPath);
             for (auto & j : wantedDepOutputs) {
                 if (outputs.count(j) > 0) {
                     auto optRealizedInput = outputs.at(j);
diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc
index 732d4785d6ee2c8dbdc91c159dd3a5cebda15179..96deb81d1e7be846f9e651e0392fac1e92bcc656 100644
--- a/src/libstore/build/entry-points.cc
+++ b/src/libstore/build/entry-points.cc
@@ -6,9 +6,9 @@
 
 namespace nix {
 
-void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode)
+void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore)
 {
-    Worker worker(*this);
+    Worker worker(*this, evalStore ? *evalStore : *this);
 
     Goals goals;
     for (auto & br : reqs) {
@@ -51,7 +51,7 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
 BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
     BuildMode buildMode)
 {
-    Worker worker(*this);
+    Worker worker(*this, *this);
     auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode);
 
     BuildResult result;
@@ -93,7 +93,7 @@ void Store::ensurePath(const StorePath & path)
     /* If the path is already valid, we're done. */
     if (isValidPath(path)) return;
 
-    Worker worker(*this);
+    Worker worker(*this, *this);
     GoalPtr goal = worker.makePathSubstitutionGoal(path);
     Goals goals = {goal};
 
@@ -111,7 +111,7 @@ void Store::ensurePath(const StorePath & path)
 
 void LocalStore::repairPath(const StorePath & path)
 {
-    Worker worker(*this);
+    Worker worker(*this, *this);
     GoalPtr goal = worker.makePathSubstitutionGoal(path, Repair);
     Goals goals = {goal};
 
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 11c99d9f08faba6003b2002912bc3e60ef9c6a32..990ff60b75f0384111810e232b7e3a7fe66a6790 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -1254,8 +1254,10 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
         return next->queryRealisation(id);
     }
 
-    void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override
+    void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
     {
+        assert(!evalStore);
+
         if (buildMode != bmNormal) throw Error("unsupported build mode");
 
         StorePathSet newPaths;
diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc
index 0f2ade348d15c687f28fc642408e39b43da15073..a7a6b92a6c001b3a3531d0199fa87639c3c5ef5e 100644
--- a/src/libstore/build/worker.cc
+++ b/src/libstore/build/worker.cc
@@ -9,11 +9,12 @@
 
 namespace nix {
 
-Worker::Worker(Store & store)
+Worker::Worker(Store & store, Store & evalStore)
     : act(*logger, actRealise)
     , actDerivations(*logger, actBuilds)
     , actSubstitutions(*logger, actCopyPaths)
     , store(store)
+    , evalStore(evalStore)
 {
     /* Debugging: prevent recursive workers. */
     nrLocalBuilds = 0;
diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh
index 918de35f6dc5d7902c4b3681e540c9dce60e9713..6a3b99c02d10fa11c10e790f262e9397522d8d12 100644
--- a/src/libstore/build/worker.hh
+++ b/src/libstore/build/worker.hh
@@ -110,6 +110,7 @@ public:
     bool checkMismatch;
 
     Store & store;
+    Store & evalStore;
 
     std::unique_ptr<HookInstance> hook;
 
@@ -131,7 +132,7 @@ public:
        it answers with "decline-permanently", we don't try again. */
     bool tryBuildHook = true;
 
-    Worker(Store & store);
+    Worker(Store & store, Store & evalStore);
     ~Worker();
 
     /* Make a goal (with caching). */
diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc
index edaf751367eed102089e1d89f166f125856e7726..45eed57075649279aea28e919828130ad790bd6d 100644
--- a/src/libstore/legacy-ssh-store.cc
+++ b/src/libstore/legacy-ssh-store.cc
@@ -267,8 +267,11 @@ public:
         return status;
     }
 
-    void buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode) override
+    void buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
     {
+        if (evalStore && evalStore.get() != this)
+            throw Error("building on an SSH store is incompatible with '--eval-store'");
+
         auto conn(connections->get());
 
         conn->to << cmdBuildPaths;
diff --git a/src/libstore/realisation.hh b/src/libstore/realisation.hh
index 05d2bc44f1d10762a092a939e1f637dea66eada7..9070a6ee21d43fe44b491f129a00958dd24a8ca0 100644
--- a/src/libstore/realisation.hh
+++ b/src/libstore/realisation.hh
@@ -45,7 +45,7 @@ struct Realisation {
     size_t checkSignatures(const PublicKeys & publicKeys) const;
 
     static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
-    static void closure(Store &, const std::set<Realisation> &, std::set<Realisation>& res);
+    static void closure(Store &, const std::set<Realisation> &, std::set<Realisation> & res);
 
     bool isCompatibleWith(const Realisation & other) const;
 
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index cd2d718eceb948e4d4da85650ac8c2d57b9889b5..77b34d000190ff8a11177a54c6844b91e2047356 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -705,8 +705,11 @@ static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, cons
     }
 }
 
-void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode)
+void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
 {
+    if (evalStore && evalStore.get() != this)
+        throw Error("building on a remote store is incompatible with '--eval-store'");
+
     auto conn(getConnection());
     conn->to << wopBuildPaths;
     assert(GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13);
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index 293393c32ff2f4e9b1d7d559caa482cc9d7bfe50..fbec40b4bcb72be2eb321a70070ae49a2677e831 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -85,7 +85,7 @@ public:
 
     std::optional<const Realisation> queryRealisation(const DrvOutput &) override;
 
-    void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode) override;
+    void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
 
     BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
         BuildMode buildMode) override;
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 80edac2442d07684744d7daa4a470820ea731ac3..3ad74f35cc4cbb6a99449dd8ff5dfdd9cc3a2c40 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -497,7 +497,8 @@ public:
        not derivations, substitute them. */
     virtual void buildPaths(
         const std::vector<DerivedPath> & paths,
-        BuildMode buildMode = bmNormal);
+        BuildMode buildMode = bmNormal,
+        std::shared_ptr<Store> evalStore = nullptr);
 
     /* Build a single non-materialized derivation (i.e. not from an
        on-disk .drv file).
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index 3b791e61918b7a78436df0e56071b5a3f2feb56c..848b108dd26c605211bdf3a2336d0d39dedc64ca 100755
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -341,7 +341,7 @@ static void main_nix_build(int argc, char * * argv)
             printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize);
 
         if (!dryRun)
-            store->buildPaths(paths, buildMode);
+            store->buildPaths(paths, buildMode, evalStore);
     };
 
     if (runEnv) {
@@ -397,8 +397,6 @@ static void main_nix_build(int argc, char * * argv)
             pathsToCopy.insert(src);
         }
 
-        copyClosure(*evalStore, *store, pathsToCopy);
-
         buildPaths(pathsToBuild);
 
         if (dryRun) return;
@@ -449,7 +447,7 @@ static void main_nix_build(int argc, char * * argv)
         if (env.count("__json")) {
             StorePathSet inputs;
             for (auto & [depDrvPath, wantedDepOutputs] : drv.inputDrvs) {
-                auto outputs = store->queryPartialDerivationOutputMap(depDrvPath);
+                auto outputs = evalStore->queryPartialDerivationOutputMap(depDrvPath);
                 for (auto & i : wantedDepOutputs) {
                     auto o = outputs.at(i);
                     store->computeFSClosure(*o, inputs);
@@ -564,8 +562,6 @@ static void main_nix_build(int argc, char * * argv)
                 drvMap[drvPath] = {drvMap.size(), {outputName}};
         }
 
-        copyClosure(*evalStore, *store, drvsToCopy);
-
         buildPaths(pathsToBuild);
 
         if (dryRun) return;
@@ -578,7 +574,7 @@ static void main_nix_build(int argc, char * * argv)
             if (counter)
                 drvPrefix += fmt("-%d", counter + 1);
 
-            auto builtOutputs = store->queryPartialDerivationOutputMap(drvPath);
+            auto builtOutputs = evalStore->queryPartialDerivationOutputMap(drvPath);
 
             auto maybeOutputPath = builtOutputs.at(outputName);
             assert(maybeOutputPath);
diff --git a/src/nix/develop.cc b/src/nix/develop.cc
index cb173b91b1cee49ab1cc3e553ca62e0e914d6fac..9a93cdb03a7b906151fefecce6a0fb586ef361b1 100644
--- a/src/nix/develop.cc
+++ b/src/nix/develop.cc
@@ -171,15 +171,15 @@ const static std::string getEnvSh =
    modified derivation with the same dependencies and nearly the same
    initial environment variables, that just writes the resulting
    environment to a file and exits. */
-StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
+static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore, const StorePath & drvPath)
 {
-    auto drv = store->derivationFromPath(drvPath);
+    auto drv = evalStore->derivationFromPath(drvPath);
 
     auto builder = baseNameOf(drv.builder);
     if (builder != "bash")
         throw Error("'nix develop' only works on derivations that use 'bash' as their builder");
 
-    auto getEnvShPath = store->addTextToStore("get-env.sh", getEnvSh, {});
+    auto getEnvShPath = evalStore->addTextToStore("get-env.sh", getEnvSh, {});
 
     drv.args = {store->printStorePath(getEnvShPath)};
 
@@ -205,7 +205,7 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
             output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } };
             drv.env[output.first] = "";
         }
-        Hash h = std::get<0>(hashDerivationModulo(*store, drv, true));
+        Hash h = std::get<0>(hashDerivationModulo(*evalStore, drv, true));
 
         for (auto & output : drv.outputs) {
             auto outPath = store->makeOutputPath(output.first, h, drv.name);
@@ -214,12 +214,12 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
         }
     }
 
-    auto shellDrvPath = writeDerivation(*store, drv);
+    auto shellDrvPath = writeDerivation(*evalStore, drv);
 
     /* Build the derivation. */
-    store->buildPaths({DerivedPath::Built{shellDrvPath}});
+    store->buildPaths({DerivedPath::Built{shellDrvPath}}, bmNormal, evalStore);
 
-    for (auto & [_0, optPath] : store->queryPartialDerivationOutputMap(shellDrvPath)) {
+    for (auto & [_0, optPath] : evalStore->queryPartialDerivationOutputMap(shellDrvPath)) {
         assert(optPath);
         auto & outPath = *optPath;
         assert(store->isValidPath(outPath));
@@ -347,7 +347,7 @@ struct Common : InstallableCommand, MixProfile
 
             auto & drvPath = *drvs.begin();
 
-            return getDerivationEnvironment(store, drvPath);
+            return getDerivationEnvironment(store, getEvalStore(), drvPath);
         }
     }
 
@@ -361,7 +361,7 @@ struct Common : InstallableCommand, MixProfile
 
         debug("reading environment file '%s'", strPath);
 
-        return {BuildEnvironment::fromJSON(readFile(strPath)), strPath};
+        return {BuildEnvironment::fromJSON(readFile(store->toRealPath(shellOutPath))), strPath};
     }
 };