diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index aaacf828152d802db2ceb9274144c9d93483faa5..717faec92711f701d6152882dd3063b9a1112d05 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -49,9 +49,9 @@ void BinaryCacheStore::init()
                     throw Error(format("binary cache '%s' is for Nix stores with prefix '%s', not '%s'")
                         % getUri() % value % storeDir);
             } else if (name == "WantMassQuery") {
-                wantMassQuery_ = value == "1";
+                wantMassQuery.setDefault(value == "1" ? "true" : "false");
             } else if (name == "Priority") {
-                string2Int(value, priority);
+                priority.setDefault(fmt("%d", std::stoi(value)));
             }
         }
     }
diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh
index fa2200ad8b60345db3bf6c240b8f2eb278a7012f..aa13c1cb454aa81409b83ea44fe16ef714187039 100644
--- a/src/libstore/binary-cache-store.hh
+++ b/src/libstore/binary-cache-store.hh
@@ -52,11 +52,6 @@ public:
 
     std::shared_ptr<std::string> getFile(const std::string & path);
 
-protected:
-
-    bool wantMassQuery_ = false;
-    int priority = 50;
-
 public:
 
     virtual void init();
@@ -79,8 +74,6 @@ public:
     std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
     { unsupported("queryPathFromHashPart"); }
 
-    bool wantMassQuery() override { return wantMassQuery_; }
-
     void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
         RepairFlag repair, CheckSigsFlag checkSigs,
         std::shared_ptr<FSAccessor> accessor) override;
@@ -107,8 +100,6 @@ public:
 
     std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
 
-    int getPriority() override { return priority; }
-
 };
 
 MakeError(NoSuchBinaryCacheFile, Error);
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 6fedf3d56c2a9f370279cfd6ea52906a662761d3..cec85edcab7c4a69b39365163fc69df769379c89 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -127,7 +127,7 @@ template<> void BaseSetting<SandboxMode>::set(const std::string & str)
     else throw UsageError("option '%s' has invalid value '%s'", name, str);
 }
 
-template<> std::string BaseSetting<SandboxMode>::to_string()
+template<> std::string BaseSetting<SandboxMode>::to_string() const
 {
     if (value == smEnabled) return "true";
     else if (value == smRelaxed) return "relaxed";
diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc
index 779f89e68d9c207ef294873995942f4c83aabed1..d4ae3666255a9ba74c3312653b75cf26e43442f3 100644
--- a/src/libstore/http-binary-cache-store.cc
+++ b/src/libstore/http-binary-cache-store.cc
@@ -42,13 +42,16 @@ public:
     void init() override
     {
         // FIXME: do this lazily?
-        if (!diskCache->cacheExists(cacheUri, wantMassQuery_, priority)) {
+        if (auto cacheInfo = diskCache->cacheExists(cacheUri)) {
+            wantMassQuery.setDefault(cacheInfo->wantMassQuery ? "true" : "false");
+            priority.setDefault(fmt("%d", cacheInfo->priority));
+        } else {
             try {
                 BinaryCacheStore::init();
             } catch (UploadToHTTP &) {
                 throw Error("'%s' does not appear to be a binary cache", cacheUri);
             }
-            diskCache->createCache(cacheUri, storeDir, wantMassQuery_, priority);
+            diskCache->createCache(cacheUri, storeDir, wantMassQuery, priority);
         }
     }
 
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index c1380ef097707d9a25aec58444f5f1632a95cc92..b254d766a58ddeac569a3f358acd1b1f1f7d3f24 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -840,7 +840,7 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
     for (auto & sub : getDefaultSubstituters()) {
         if (remaining.empty()) break;
         if (sub->storeDir != storeDir) continue;
-        if (!sub->wantMassQuery()) continue;
+        if (!sub->wantMassQuery) continue;
 
         auto valid = sub->queryValidPaths(remaining);
 
diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc
index 3a2cde55f2036fbf395a0c5fb8cfb24de0f4b3c4..907645d8671092a72d64c280a757b78ec9efd667 100644
--- a/src/libstore/nar-info-disk-cache.cc
+++ b/src/libstore/nar-info-disk-cache.cc
@@ -147,26 +147,26 @@ public:
         });
     }
 
-    bool cacheExists(const std::string & uri,
-        bool & wantMassQuery, int & priority) override
+    std::optional<CacheInfo> cacheExists(const std::string & uri) override
     {
-        return retrySQLite<bool>([&]() {
+        return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
             auto state(_state.lock());
 
             auto i = state->caches.find(uri);
             if (i == state->caches.end()) {
                 auto queryCache(state->queryCache.use()(uri));
-                if (!queryCache.next()) return false;
+                if (!queryCache.next())
+                    return std::nullopt;
                 state->caches.emplace(uri,
                     Cache{(int) queryCache.getInt(0), queryCache.getStr(1), queryCache.getInt(2) != 0, (int) queryCache.getInt(3)});
             }
 
             auto & cache(getCache(*state, uri));
 
-            wantMassQuery = cache.wantMassQuery;
-            priority = cache.priority;
-
-            return true;
+            return CacheInfo {
+                .wantMassQuery = cache.wantMassQuery,
+                .priority = cache.priority
+            };
         });
     }
 
diff --git a/src/libstore/nar-info-disk-cache.hh b/src/libstore/nar-info-disk-cache.hh
index 285873f7edec61f57b486c31bead0935f8117c01..878acbb87b5821babef471b582a426e5f057eeec 100644
--- a/src/libstore/nar-info-disk-cache.hh
+++ b/src/libstore/nar-info-disk-cache.hh
@@ -15,8 +15,13 @@ public:
     virtual void createCache(const std::string & uri, const Path & storeDir,
         bool wantMassQuery, int priority) = 0;
 
-    virtual bool cacheExists(const std::string & uri,
-        bool & wantMassQuery, int & priority) = 0;
+    struct CacheInfo
+    {
+        bool wantMassQuery;
+        int priority;
+    };
+
+    virtual std::optional<CacheInfo> cacheExists(const std::string & uri) = 0;
 
     virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
         const std::string & uri, const std::string & hashPart) = 0;
diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc
index 58966a5b796854f448e7f5152d94b23510f95f2f..f2e4b63e0593a1e7fef6aa9f946b042e1366ab7f 100644
--- a/src/libstore/s3-binary-cache-store.cc
+++ b/src/libstore/s3-binary-cache-store.cc
@@ -205,11 +205,12 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
 
     void init() override
     {
-        if (!diskCache->cacheExists(getUri(), wantMassQuery_, priority)) {
-
+        if (auto cacheInfo = diskCache->cacheExists(getUri())) {
+            wantMassQuery.setDefault(cacheInfo->wantMassQuery ? "true" : "false");
+            priority.setDefault(fmt("%d", cacheInfo->priority));
+        } else {
             BinaryCacheStore::init();
-
-            diskCache->createCache(getUri(), storeDir, wantMassQuery_, priority);
+            diskCache->createCache(getUri(), storeDir, wantMassQuery, priority);
         }
     }
 
diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh
index 115679b84159cf37abe171199ef8027c8f0bbd5f..bd012d9b9636c071f1ee21c3847eca288ad0ab70 100644
--- a/src/libstore/sqlite.hh
+++ b/src/libstore/sqlite.hh
@@ -99,8 +99,8 @@ void handleSQLiteBusy(const SQLiteBusy & e);
 
 /* Convenience function for retrying a SQLite transaction when the
    database is busy. */
-template<typename T>
-T retrySQLite(std::function<T()> fun)
+template<typename T, typename F>
+T retrySQLite(F && fun)
 {
     while (true) {
         try {
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 1781e0c6839c68f7eb4996d1b409d73b75208f24..d8f6c22bce3eaf236d3448cc2389c4421438ce7a 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -975,7 +975,7 @@ std::list<ref<Store>> getDefaultSubstituters()
             addStore(uri);
 
         stores.sort([](ref<Store> & a, ref<Store> & b) {
-            return a->getPriority() < b->getPriority();
+            return a->priority < b->priority;
         });
 
         return stores;
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index d77d6bad8301398f0b126d46468927761c35a902..743be94108d85ae5c3f99226da6651cb33a81e40 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -255,6 +255,10 @@ public:
 
     const Setting<bool> isTrusted{this, false, "trusted", "whether paths from this store can be used as substitutes even when they lack trusted signatures"};
 
+    Setting<int> priority{this, 0, "priority", "priority of this substituter (lower value means higher priority)"};
+
+    Setting<bool> wantMassQuery{this, false, "want-mass-query", "whether this substituter can be queried efficiently for path validity"};
+
 protected:
 
     struct State
@@ -422,8 +426,6 @@ public:
     virtual void querySubstitutablePathInfos(const StorePathSet & paths,
         SubstitutablePathInfos & infos) { return; };
 
-    virtual bool wantMassQuery() { return false; }
-
     /* Import a path into the store. */
     virtual void addToStore(const ValidPathInfo & info, Source & narSource,
         RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs,
@@ -648,11 +650,6 @@ public:
         return 0;
     };
 
-    /* Get the priority of the store, used to order substituters. In
-       particular, binary caches can specify a priority field in their
-       "nix-cache-info" file. Lower value means higher priority. */
-    virtual int getPriority() { return 0; }
-
     virtual Path toRealPath(const Path & storePath)
     {
         return storePath;
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index 9023cb1bb6dec87fbbb1d132014bff10593b300c..7551d97d1a7e5e4beb18d5bf21e61f598b4230bd 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -154,6 +154,11 @@ AbstractSetting::AbstractSetting(
 {
 }
 
+void AbstractSetting::setDefault(const std::string & str)
+{
+    if (!overriden) set(str);
+}
+
 void AbstractSetting::toJSON(JSONPlaceholder & out)
 {
     out.write(to_string());
@@ -185,7 +190,7 @@ template<> void BaseSetting<std::string>::set(const std::string & str)
     value = str;
 }
 
-template<> std::string BaseSetting<std::string>::to_string()
+template<> std::string BaseSetting<std::string>::to_string() const
 {
     return value;
 }
@@ -199,7 +204,7 @@ void BaseSetting<T>::set(const std::string & str)
 }
 
 template<typename T>
-std::string BaseSetting<T>::to_string()
+std::string BaseSetting<T>::to_string() const
 {
     static_assert(std::is_integral<T>::value, "Integer required.");
     return std::to_string(value);
@@ -215,7 +220,7 @@ template<> void BaseSetting<bool>::set(const std::string & str)
         throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str);
 }
 
-template<> std::string BaseSetting<bool>::to_string()
+template<> std::string BaseSetting<bool>::to_string() const
 {
     return value ? "true" : "false";
 }
@@ -239,7 +244,7 @@ template<> void BaseSetting<Strings>::set(const std::string & str)
     value = tokenizeString<Strings>(str);
 }
 
-template<> std::string BaseSetting<Strings>::to_string()
+template<> std::string BaseSetting<Strings>::to_string() const
 {
     return concatStringsSep(" ", value);
 }
@@ -256,7 +261,7 @@ template<> void BaseSetting<StringSet>::set(const std::string & str)
     value = tokenizeString<StringSet>(str);
 }
 
-template<> std::string BaseSetting<StringSet>::to_string()
+template<> std::string BaseSetting<StringSet>::to_string() const
 {
     return concatStringsSep(" ", value);
 }
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index d86c65ff033a42eb785404b5811be1490da9b4d9..7ea78fdaf5c432133e37b70c43462cb944e86206 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -115,6 +115,8 @@ public:
 
     bool overriden = false;
 
+    void setDefault(const std::string & str);
+
 protected:
 
     AbstractSetting(
@@ -131,13 +133,13 @@ protected:
 
     virtual void set(const std::string & value) = 0;
 
-    virtual std::string to_string() = 0;
+    virtual std::string to_string() const = 0;
 
     virtual void toJSON(JSONPlaceholder & out);
 
     virtual void convertToArg(Args & args, const std::string & category);
 
-    bool isOverriden() { return overriden; }
+    bool isOverriden() const { return overriden; }
 };
 
 /* A setting of type T. */
@@ -174,7 +176,7 @@ public:
         value = v;
     }
 
-    std::string to_string() override;
+    std::string to_string() const override;
 
     void convertToArg(Args & args, const std::string & category) override;