diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 1abe236f71b84aafe7aed318cda3dde9651fa5bf..e92b94975de3d304c1509b41c0dbca2a30b6d93c 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -97,7 +97,16 @@ RemoteStore::RemoteStore(const Params & params)
     , RemoteStoreConfig(params)
     , connections(make_ref<Pool<Connection>>(
             std::max(1, (int) maxConnections),
-            [this]() { return openConnectionWrapper(); },
+            [this]() {
+                auto conn = openConnectionWrapper();
+                try {
+                    initConnection(*conn);
+                } catch (...) {
+                    failed = true;
+                    throw;
+                }
+                return conn;
+            },
             [this](const ref<Connection> & r) {
                 return
                     r->to.good()
@@ -182,8 +191,6 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
 
     conn->startTime = std::chrono::steady_clock::now();
 
-    initConnection(*conn);
-
     return conn;
 }
 
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index a2369083045c04ae492159e4db26c1af697a47c3..91c7480067457cc035e0053f0fea96feff91fc5a 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -106,8 +106,6 @@ public:
 
     void flushBadConnections();
 
-protected:
-
     struct Connection
     {
         AutoCloseFD fd;
@@ -123,6 +121,8 @@ protected:
 
     ref<Connection> openConnectionWrapper();
 
+protected:
+
     virtual ref<Connection> openConnection() = 0;
 
     void initConnection(Connection & conn);
diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc
index 8b6e48fb0a3e03c0278e6968dd3e069b3c6149d6..6d6eca98d78fb771f10d0b9b3914f247a8d4d404 100644
--- a/src/libstore/ssh-store.cc
+++ b/src/libstore/ssh-store.cc
@@ -80,7 +80,6 @@ ref<RemoteStore::Connection> SSHStore::openConnection()
         + (remoteStore.get() == "" ? "" : " --store " + shellEscape(remoteStore.get())));
     conn->to = FdSink(conn->sshConn->in.get());
     conn->from = FdSource(conn->sshConn->out.get());
-    initConnection(*conn);
     return conn;
 }
 
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 76cbc0605b06c3280563de592bca9f267efb28f7..2d5077ed033929a769e906e713e1804a6367ea43 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -1040,38 +1040,26 @@ static bool isNonUriPath(const std::string & spec) {
         && spec.find("/") != std::string::npos;
 }
 
-StoreType getStoreType(const std::string & uri, const std::string & stateDir)
+std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params)
 {
-    if (uri == "daemon") {
-        return tDaemon;
-    } else if (uri == "local" || isNonUriPath(uri)) {
-        return tLocal;
-    } else if (uri == "" || uri == "auto") {
+    if (uri == "" || uri == "auto") {
+        auto stateDir = get(params, "state").value_or(settings.nixStateDir);
         if (access(stateDir.c_str(), R_OK | W_OK) == 0)
-            return tLocal;
+            return std::make_shared<LocalStore>(params);
         else if (pathExists(settings.nixDaemonSocketFile))
-            return tDaemon;
+            return std::make_shared<UDSRemoteStore>(params);
         else
-            return tLocal;
+            return std::make_shared<LocalStore>(params);
+    } else if (uri == "daemon") {
+        return std::make_shared<UDSRemoteStore>(params);
+    } else if (uri == "local") {
+        return std::make_shared<LocalStore>(params);
+    } else if (isNonUriPath(uri)) {
+        Store::Params params2 = params;
+        params2["root"] = absPath(uri);
+        return std::make_shared<LocalStore>(params2);
     } else {
-        return tOther;
-    }
-}
-
-std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params)
-{
-    switch (getStoreType(uri, get(params, "state").value_or(settings.nixStateDir))) {
-        case tDaemon:
-            return std::shared_ptr<Store>(std::make_shared<UDSRemoteStore>(params));
-        case tLocal: {
-            Store::Params params2 = params;
-            if (isNonUriPath(uri)) {
-                params2["root"] = absPath(uri);
-            }
-            return std::shared_ptr<Store>(std::make_shared<LocalStore>(params2));
-        }
-        default:
-            return nullptr;
+        return nullptr;
     }
 }
 
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 1bea4837b341751c3fc5a63148ed3ce6d2991961..4d3f07dfc3712dda37a70fce65ca86ca7abdf84a 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -791,16 +791,6 @@ ref<Store> openStore(const std::string & uri = settings.storeUri.get(),
     const Store::Params & extraParams = Store::Params());
 
 
-enum StoreType {
-    tDaemon,
-    tLocal,
-    tOther
-};
-
-
-StoreType getStoreType(const std::string & uri = settings.storeUri.get(),
-    const std::string & stateDir = settings.nixStateDir);
-
 /* Return the default substituter stores, defined by the
    ‘substituters’ option and various legacy options. */
 std::list<ref<Store>> getDefaultSubstituters();
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index cfa634a4413f4df58c5eb13cdb1b9be2df7abed8..6e652ccbf0a10c3b00cdcb90ec9878840d34f8e5 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -1,5 +1,6 @@
 #include "shared.hh"
 #include "local-store.hh"
+#include "remote-store.hh"
 #include "util.hh"
 #include "serialise.hh"
 #include "archive.hh"
@@ -285,44 +286,28 @@ static int _main(int argc, char * * argv)
         initPlugins();
 
         if (stdio) {
-            if (getStoreType() == tDaemon) {
-                //  Forward on this connection to the real daemon
-                auto socketPath = settings.nixDaemonSocketFile;
-                auto s = socket(PF_UNIX, SOCK_STREAM, 0);
-                if (s == -1)
-                    throw SysError("creating Unix domain socket");
-
-                auto socketDir = dirOf(socketPath);
-                if (chdir(socketDir.c_str()) == -1)
-                    throw SysError("changing to socket directory '%1%'", socketDir);
-
-                auto socketName = std::string(baseNameOf(socketPath));
-                auto addr = sockaddr_un{};
-                addr.sun_family = AF_UNIX;
-                if (socketName.size() + 1 >= sizeof(addr.sun_path))
-                    throw Error("socket name %1% is too long", socketName);
-                strcpy(addr.sun_path, socketName.c_str());
-
-                if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) == -1)
-                    throw SysError("cannot connect to daemon at %1%", socketPath);
-
-                auto nfds = (s > STDIN_FILENO ? s : STDIN_FILENO) + 1;
+            if (auto store = openUncachedStore().dynamic_pointer_cast<RemoteStore>()) {
+                auto conn = store->openConnectionWrapper();
+                int from = conn->from.fd;
+                int to = conn->to.fd;
+
+                auto nfds = std::max(from, STDIN_FILENO) + 1;
                 while (true) {
                     fd_set fds;
                     FD_ZERO(&fds);
-                    FD_SET(s, &fds);
+                    FD_SET(from, &fds);
                     FD_SET(STDIN_FILENO, &fds);
                     if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
                         throw SysError("waiting for data from client or server");
-                    if (FD_ISSET(s, &fds)) {
-                        auto res = splice(s, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
+                    if (FD_ISSET(from, &fds)) {
+                        auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
                         if (res == -1)
                             throw SysError("splicing data from daemon socket to stdout");
                         else if (res == 0)
                             throw EndOfFile("unexpected EOF from daemon socket");
                     }
                     if (FD_ISSET(STDIN_FILENO, &fds)) {
-                        auto res = splice(STDIN_FILENO, nullptr, s, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
+                        auto res = splice(STDIN_FILENO, nullptr, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
                         if (res == -1)
                             throw SysError("splicing data from stdin to daemon socket");
                         else if (res == 0)
diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc
index 82e92cdd0a923359c30547c9aa00e6081e4bd75f..683e9144602628b713b8a1e597a4be589443565f 100644
--- a/src/nix/doctor.cc
+++ b/src/nix/doctor.cc
@@ -49,9 +49,7 @@ struct CmdDoctor : StoreCommand
     {
         logger->log("Running checks against store uri: " + store->getUri());
 
-        auto type = getStoreType();
-
-        if (type < tOther) {
+        if (store.dynamic_pointer_cast<LocalFSStore>()) {
             success &= checkNixInPath();
             success &= checkProfileRoots(store);
         }
diff --git a/tests/local.mk b/tests/local.mk
index 5949015041fb94c631e2336483b6aca6119818f5..a1929f96d29309f85491b6c1e6b34259194c3a2d 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -15,6 +15,7 @@ nix_tests = \
   linux-sandbox.sh \
   build-dry.sh \
   build-remote-input-addressed.sh \
+  ssh-relay.sh \
   nar-access.sh \
   structured-attrs.sh \
   fetchGit.sh \
diff --git a/tests/ssh-relay.sh b/tests/ssh-relay.sh
new file mode 100644
index 0000000000000000000000000000000000000000..dce50974b16cefcf129fcf838d83a489b733b401
--- /dev/null
+++ b/tests/ssh-relay.sh
@@ -0,0 +1,16 @@
+source common.sh
+
+echo foo > $TEST_ROOT/hello.sh
+
+ssh_localhost=ssh://localhost
+remote_store=?remote-store=$ssh_localhost
+
+store=$ssh_localhost
+
+store+=$remote_store
+store+=$remote_store
+store+=$remote_store
+
+out=$(nix add-to-store --store "$store" $TEST_ROOT/hello.sh)
+
+[ foo = $(< $out) ]