diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
+## 0.2.0.0 - 2021-06-22
+
+### Added
+
+ - nixUnstable compatibility with flag `nix-2_4`
+
 ## 0.1.0.0 - 2021-03-07
 
 ### Added
diff --git a/hercules-ci-cnix-expr.cabal b/hercules-ci-cnix-expr.cabal
--- a/hercules-ci-cnix-expr.cabal
+++ b/hercules-ci-cnix-expr.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.4
 
 name:           hercules-ci-cnix-expr
-version:        0.1.0.0
+version:        0.2.0.0
 synopsis:       Bindings for the Nix evaluator
 category:       Nix, CI, Testing, DevOps
 homepage:       https://docs.hercules-ci.com
@@ -18,6 +18,10 @@
   type: git
   location: https://github.com/hercules-ci/hercules-ci-agent
 
+flag nix-2_4
+  description: Build for Nix >=2.4pre*
+  default: False
+
 -- match the C++ language standard Nix is using
 common cxx-opts
   cxx-options:
@@ -65,6 +69,7 @@
     , inline-c-cpp
     , protolude >= 0.3
     , text
+    , unliftio
   default-language: Haskell2010
   include-dirs:
       include
@@ -72,8 +77,17 @@
       hercules-ci-cnix/expr.hxx
   extra-libraries:
       boost_context
-  pkgconfig-depends:
-      nix-store >= 2.0
-    , nix-expr >= 2.0
-    , nix-main >= 2.0
-    , bdw-gc
+
+  if flag(nix-2_4)
+    cpp-options:
+        -DNIX_2_4
+    pkgconfig-depends:
+        nix-store >= 2.4
+      , nix-expr >= 2.4
+      , nix-main >= 2.4
+  else
+    pkgconfig-depends:
+        nix-store >= 2.0
+      , nix-expr >= 2.0
+      , nix-main >= 2.0
+      , bdw-gc
diff --git a/src/Hercules/CNix/Expr.hs b/src/Hercules/CNix/Expr.hs
--- a/src/Hercules/CNix/Expr.hs
+++ b/src/Hercules/CNix/Expr.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TemplateHaskell #-}
 
@@ -20,6 +21,7 @@
 import qualified Data.Map as M
 import Foreign (nullPtr)
 import qualified Foreign.C.String
+import Hercules.CNix.Encapsulation (moveToForeignPtrWrapper)
 import Hercules.CNix.Expr.Context
 import Hercules.CNix.Expr.Raw
 import Hercules.CNix.Expr.Typed
@@ -28,6 +30,7 @@
 import qualified Language.C.Inline.Cpp as C
 import qualified Language.C.Inline.Cpp.Exceptions as C
 import Protolude hiding (evalState, throwIO)
+import qualified UnliftIO
 
 C.context (Hercules.CNix.Store.Context.context <> Hercules.CNix.Expr.Context.evalContext)
 
@@ -57,6 +60,8 @@
 
 C.include "<nix/globals.hh>"
 
+C.include "<nix-compat.hh>"
+
 C.include "hercules-ci-cnix/expr.hxx"
 
 C.include "<gc/gc.h>"
@@ -178,8 +183,12 @@
         throw nix::Error("Could not evaluate automatic arguments");
       }
       Value *r = new (NoGC) Value ();
+#ifdef NIX_2_4
+      r->mkAttrs(autoArgs);
+#else
       r->type = tAttrs;
       r->attrs = autoArgs;
+#endif
       return r;
     }|]
 
@@ -217,13 +226,25 @@
   (0 /=)
     <$> [C.throwBlock| int {
           Value *v = $(Value *v);
-          EvalState *evalState = $(EvalState *evalState);
-          Symbol rfd = evalState->symbols.create("recurseForDerivations");
+          EvalState &evalState = *$(EvalState *evalState);
+#ifdef NIX_2_4
+          Bindings::iterator iter = v->attrs->find(evalState.sRecurseForDerivations);
+          if (iter == v->attrs->end()) {
+            return 0;
+          } else {
+            // Previously this bool was unpacked manually and included a special
+            // case to return true when it is not a bool. That logic was added
+            // because an empty attrset was found here, observed in
+            // nixpkgs master 67e2de195a4aa0a50ffb1e1ba0b4fb531dca67dc
+            return evalState.forceBool(*iter->value, *iter->pos);
+          }
+#else
+          Symbol rfd = evalState.symbols.create("recurseForDerivations");
           Bindings::iterator iter = v->attrs->find(rfd);
           if (iter == v->attrs->end()) {
             return 0;
           } else {
-            evalState->forceValue(*iter->value);
+            evalState.forceValue(*iter->value);
             if (iter->value->type == ValueType::tBool) {
               return iter->value->boolean ? 1 : 0;
             } else {
@@ -232,6 +253,7 @@
               return 1;
             }
           }
+#endif
         } |]
 
 getAttr :: Ptr EvalState -> Value NixAttrs -> ByteString -> IO (Maybe RawValue)
@@ -268,21 +290,22 @@
         gather acc' =<< [C.exp| Attr *{ &$(Attr *i)[1] }|]
   gather mempty begin
 
-getDrvFile :: MonadIO m => Ptr EvalState -> RawValue -> m ByteString
-getDrvFile evalState (RawValue v) =
-  unsafeMallocBS
-    [C.throwBlock| const char *{
+getDrvFile :: MonadIO m => Ptr EvalState -> RawValue -> m StorePath
+getDrvFile evalState (RawValue v) = liftIO do
+  moveToForeignPtrWrapper
+    =<< [C.throwBlock| nix::StorePath *{
       EvalState &state = *$(EvalState *evalState);
       auto drvInfo = getDerivation(state, *$(Value *v), false);
       if (!drvInfo)
         throw EvalError("Not a valid derivation");
 
       std::string drvPath = drvInfo->queryDrvPath();
+      StorePath storePath = parseStorePath(*state.store, drvPath);
 
       // write it (?)
-      auto drv = state.store->derivationFromPath(drvPath);
+      auto drv = state.store->derivationFromPath(printPath23(*state.store, storePath));
 
-      return strdup(drvPath.c_str());
+      return new StorePath(storePath);
     }|]
 
 getAttrBool :: Ptr EvalState -> Value NixAttrs -> ByteString -> IO (Either SomeException (Maybe Bool))
diff --git a/src/Hercules/CNix/Expr/Raw.hs b/src/Hercules/CNix/Expr/Raw.hs
--- a/src/Hercules/CNix/Expr/Raw.hs
+++ b/src/Hercules/CNix/Expr/Raw.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TemplateHaskell #-}
 
@@ -55,9 +56,42 @@
 
 -- | You may need to 'forceValue' first.
 rawValueType :: RawValue -> IO RawValueType
+#ifdef NIX_2_4
 rawValueType (RawValue v) =
   f
     <$> [C.block| int {
+      switch ($(Value* v)->type()) {
+        case nInt:         return 1;
+        case nBool:        return 2;
+        case nString:      return 3;
+        case nPath:        return 4;
+        case nNull:        return 5;
+        case nAttrs:       return 6;
+        case nList:        return 7;
+        case nFunction:    return 8;
+        case nExternal:    return 9;
+        case nFloat:       return 10;
+        case nThunk:       return 11;
+        default: return 0;
+      }
+    }|]
+  where
+    f 1 = Int
+    f 2 = Bool
+    f 3 = String
+    f 4 = Path
+    f 5 = Null
+    f 6 = Attrs
+    f 7 = List
+    f 8 = Lambda
+    f 9 = External
+    f 10 = Float
+    f 11 = Thunk
+    f _ = Other
+#else
+rawValueType (RawValue v) =
+  f
+    <$> [C.block| int {
       switch ($(Value* v)->type) {
         case tInt:         return 1;
         case tBool:        return 2;
@@ -98,6 +132,7 @@
     f 16 = External
     f 17 = Float
     f _ = Other
+#endif
 
 forceValue :: Exception a => Ptr EvalState -> RawValue -> IO (Either a ())
 forceValue evalState (RawValue v) =
