diff --git a/Makefile.config.in b/Makefile.config.in
index d1e59e4e74763edd1459a2324f2095be0c8db583..9d0500e4810f501a1e36b824ec5ce175f88daea6 100644
--- a/Makefile.config.in
+++ b/Makefile.config.in
@@ -9,6 +9,7 @@ CXXFLAGS = @CXXFLAGS@
 EDITLINE_LIBS = @EDITLINE_LIBS@
 ENABLE_S3 = @ENABLE_S3@
 GTEST_LIBS = @GTEST_LIBS@
+HAVE_LIBCPUID = @HAVE_LIBCPUID@
 HAVE_SECCOMP = @HAVE_SECCOMP@
 LDFLAGS = @LDFLAGS@
 LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
diff --git a/configure.ac b/configure.ac
index 2047ed8d2466ff64a4a379aa89d250932ccc62d3..a24287ff6907d400355df510ae1ed53c3c3797a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -218,6 +218,14 @@ LDFLAGS="-lz $LDFLAGS"
 # Look for libbrotli{enc,dec}.
 PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"])
 
+# Look for libcpuid.
+if test "$machine_name" = "x86_64"; then
+  PKG_CHECK_MODULES([LIBCPUID], [libcpuid], [CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"])
+  have_libcpuid=1
+  AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])
+fi
+AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])
+
 
 # Look for libseccomp, required for Linux sandboxing.
 if test "$sys_name" = linux; then
diff --git a/flake.nix b/flake.nix
index 8c60934e6364f4a3d7b5f9f6c8573016e89f8138..3ad7cca97af7059c0094ec8b982697a79660f05d 100644
--- a/flake.nix
+++ b/flake.nix
@@ -91,7 +91,8 @@
             gmock
           ]
           ++ lib.optionals stdenv.isLinux [libseccomp utillinuxMinimal]
-          ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium;
+          ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
+          ++ lib.optional stdenv.isx86_64 libcpuid;
 
         awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
           (aws-sdk-cpp.override {
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 0531aad9f6c1958c4e53fde71e6d650c262ccb2e..df07aee9b9b23fad07af2324e0842595f5ed6e68 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -3,6 +3,7 @@
 #include "archive.hh"
 #include "args.hh"
 #include "abstract-setting-to-json.hh"
+#include "compute-levels.hh"
 
 #include <algorithm>
 #include <map>
@@ -133,24 +134,29 @@ StringSet Settings::getDefaultSystemFeatures()
 
 StringSet Settings::getDefaultExtraPlatforms()
 {
+    StringSet extraPlatforms;
+
     if (std::string{SYSTEM} == "x86_64-linux" && !isWSL1())
-        return StringSet{"i686-linux"};
-#if __APPLE__
+        extraPlatforms.insert("i686-linux");
+
+#if __linux__
+    StringSet levels = computeLevels();
+    for (auto iter = levels.begin(); iter != levels.end(); ++iter)
+        extraPlatforms.insert(*iter + "-linux");
+#elif __APPLE__
     // Rosetta 2 emulation layer can run x86_64 binaries on aarch64
     // machines. Note that we can’t force processes from executing
     // x86_64 in aarch64 environments or vice versa since they can
     // always exec with their own binary preferences.
-    else if (pathExists("/Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist")) {
+    if (pathExists("/Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist")) {
         if (std::string{SYSTEM} == "x86_64-darwin")
-            return StringSet{"aarch64-darwin"};
+            extraPlatforms.insert("aarch64-darwin");
         else if (std::string{SYSTEM} == "aarch64-darwin")
-            return StringSet{"x86_64-darwin"};
-        else
-            return StringSet{};
+            extraPlatforms.insert("x86_64-darwin");
     }
 #endif
-    else
-        return StringSet{};
+
+    return extraPlatforms;
 }
 
 bool Settings::isExperimentalFeatureEnabled(const std::string & name)
diff --git a/src/libutil/compute-levels.cc b/src/libutil/compute-levels.cc
new file mode 100644
index 0000000000000000000000000000000000000000..19eaedfa8d1cd202dd5d983625b8aff78dfb574a
--- /dev/null
+++ b/src/libutil/compute-levels.cc
@@ -0,0 +1,80 @@
+#include "types.hh"
+
+#if HAVE_LIBCPUID
+#include <libcpuid/libcpuid.h>
+#endif
+
+namespace nix {
+
+#if HAVE_LIBCPUID
+
+StringSet computeLevels() {
+    StringSet levels;
+
+    if (!cpuid_present())
+        return levels;
+
+    cpu_raw_data_t raw;
+    cpu_id_t data;
+
+    if (cpuid_get_raw_data(&raw) < 0)
+        return levels;
+
+    if (cpu_identify(&raw, &data) < 0)
+        return levels;
+
+    if (!(data.flags[CPU_FEATURE_CMOV] &&
+            data.flags[CPU_FEATURE_CX8] &&
+            data.flags[CPU_FEATURE_FPU] &&
+            data.flags[CPU_FEATURE_FXSR] &&
+            data.flags[CPU_FEATURE_MMX] &&
+            data.flags[CPU_FEATURE_SSE] &&
+            data.flags[CPU_FEATURE_SSE2]))
+        return levels;
+
+    levels.insert("x86_64-v1");
+
+    if (!(data.flags[CPU_FEATURE_CX16] &&
+            data.flags[CPU_FEATURE_LAHF_LM] &&
+            data.flags[CPU_FEATURE_POPCNT] &&
+            // SSE3
+            data.flags[CPU_FEATURE_PNI] &&
+            data.flags[CPU_FEATURE_SSSE3] &&
+            data.flags[CPU_FEATURE_SSE4_1] &&
+            data.flags[CPU_FEATURE_SSE4_2]))
+        return levels;
+
+    levels.insert("x86_64-v2");
+
+    if (!(data.flags[CPU_FEATURE_AVX] &&
+            data.flags[CPU_FEATURE_AVX2] &&
+            data.flags[CPU_FEATURE_F16C] &&
+            data.flags[CPU_FEATURE_FMA3] &&
+            // LZCNT
+            data.flags[CPU_FEATURE_ABM] &&
+            data.flags[CPU_FEATURE_MOVBE]))
+        return levels;
+
+    levels.insert("x86_64-v3");
+
+    if (!(data.flags[CPU_FEATURE_AVX512F] &&
+            data.flags[CPU_FEATURE_AVX512BW] &&
+            data.flags[CPU_FEATURE_AVX512CD] &&
+            data.flags[CPU_FEATURE_AVX512DQ] &&
+            data.flags[CPU_FEATURE_AVX512VL]))
+        return levels;
+
+    levels.insert("x86_64-v4");
+
+    return levels;
+}
+
+#else
+
+StringSet computeLevels() {
+    return StringSet{};
+}
+
+#endif // HAVE_LIBCPUID
+
+}
diff --git a/src/libutil/compute-levels.hh b/src/libutil/compute-levels.hh
new file mode 100644
index 0000000000000000000000000000000000000000..8ded295f9e2241d62b389687c18c5a46f9a9307c
--- /dev/null
+++ b/src/libutil/compute-levels.hh
@@ -0,0 +1,7 @@
+#include "types.hh"
+
+namespace nix {
+
+StringSet computeLevels();
+
+}
diff --git a/src/libutil/local.mk b/src/libutil/local.mk
index ae7eb67adf6a243d0d51117849c9b1a7e8b4357c..5341c58e60ae900971ef7a662326d427a084d628 100644
--- a/src/libutil/local.mk
+++ b/src/libutil/local.mk
@@ -7,3 +7,7 @@ libutil_DIR := $(d)
 libutil_SOURCES := $(wildcard $(d)/*.cc)
 
 libutil_LDFLAGS = $(LIBLZMA_LIBS) -lbz2 -pthread $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
+
+ifeq ($(HAVE_LIBCPUID), 1)
+	libutil_LDFLAGS += -lcpuid
+endif
diff --git a/tests/compute-levels.sh b/tests/compute-levels.sh
new file mode 100644
index 0000000000000000000000000000000000000000..e4322dfa1d8f2abe21abf61279fe2e8927b4d695
--- /dev/null
+++ b/tests/compute-levels.sh
@@ -0,0 +1,7 @@
+source common.sh
+
+if [[ $(uname -ms) = "Linux x86_64" ]]; then
+    # x86_64 CPUs must always support the baseline
+    # microarchitecture level.
+    nix -vv --version | grep -q "x86_64-v1-linux"
+fi
diff --git a/tests/local.mk b/tests/local.mk
index aa8b4f9bf913a9795d495a7cc21dfdfe81d06445..06be8cec1c13edf292efaa06b63c77969de7d04f 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -38,7 +38,8 @@ nix_tests = \
   describe-stores.sh \
   flakes.sh \
   content-addressed.sh \
-  build.sh
+  build.sh \
+  compute-levels.sh
   # parallel.sh
   # build-remote-content-addressed-fixed.sh \