diff --git a/.envrc b/.envrc
index e080e228..56451c1a 100644
--- a/.envrc
+++ b/.envrc
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-use flake
+use flake ".#${DIRENV_DEVSHELL:-default}"
 
 PATH_add bin
 
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fd13c073..14474c7a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,6 +35,9 @@ env:
     # Custom nix binary cache if fork is being used
     ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
     ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
+    # Use the all-features devshell instead of default, to ensure that features
+    # match between nix and cargo
+    DIRENV_DEVSHELL: all-features
 
 permissions:
     packages: write
@@ -92,7 +95,7 @@ jobs:
                   echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
                   nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
                   direnv allow
-                  nix develop --command true
+                  nix develop .#all-features --command true
 
             - name: Run CI tests
               run: |
@@ -202,7 +205,7 @@ jobs:
                   echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
                   nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
                   direnv allow
-                  nix develop --command true
+                  nix develop .#all-features --command true
 
             - name: Build static ${{ matrix.target }}
               run: |
diff --git a/bin/nix-build-and-cache b/bin/nix-build-and-cache
index 67e3aa4e..f74a64e9 100755
--- a/bin/nix-build-and-cache
+++ b/bin/nix-build-and-cache
@@ -70,7 +70,7 @@ ci() {
         --inputs-from "$toplevel"
 
         # Keep sorted
-        "$toplevel#devShells.x86_64-linux.default"
+        "$toplevel#devShells.x86_64-linux.all-features"
         attic#default
         nixpkgs#direnv
         nixpkgs#jq
diff --git a/flake.nix b/flake.nix
index 08e3c954..4dafcd95 100644
--- a/flake.nix
+++ b/flake.nix
@@ -188,5 +188,9 @@
         );
 
       devShells.default = mkDevShell scopeHostStatic;
+      devShells.all-features = mkDevShell
+        (scopeHostStatic.overrideScope (final: prev: {
+          main = prev.main.override { all_features = true; };
+        }));
     });
 }
diff --git a/nix/pkgs/main/default.nix b/nix/pkgs/main/default.nix
index 9eba2a87..87f260f8 100644
--- a/nix/pkgs/main/default.nix
+++ b/nix/pkgs/main/default.nix
@@ -11,6 +11,7 @@
 
 # Options (keep sorted)
 , default_features ? true
+, all_features ? false
 , features ? []
 , profile ? "release"
 }:
@@ -20,13 +21,23 @@ let
 # on the nix side depend on feature values.
 workspaceMembers = builtins.map (member: "${inputs.self}/src/${member}")
   (builtins.attrNames (builtins.readDir "${inputs.self}/src"));
+crateFeatures = path:
+  let manifest = lib.importTOML "${path}/Cargo.toml"; in
+  lib.remove "default" (lib.attrNames manifest.features) ++
+  lib.attrNames
+    (lib.filterAttrs
+      (_: dependency: dependency.optional or false)
+      manifest.dependencies);
 crateDefaultFeatures = path:
   (lib.importTOML "${path}/Cargo.toml").features.default;
 allDefaultFeatures = lib.unique
   (lib.flatten (builtins.map crateDefaultFeatures workspaceMembers));
+allFeatures = lib.unique
+  (lib.flatten (builtins.map crateFeatures workspaceMembers));
 features' = lib.unique
   (features ++
-    lib.optionals default_features allDefaultFeatures);
+    lib.optionals default_features allDefaultFeatures ++
+    lib.optionals all_features allFeatures);
 
 featureEnabled = feature : builtins.elem feature features';