diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,20 @@
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
+## 0.2.1.0 - 2021-09-06
+
+### Added
+
+ - `installDefaultSigINTHandler`
+
+### Fixed
+
+ - Interrupt handling
+
+### Changed
+
+ - Updated to work with a newer `nixUnstable`
+
 ## 0.2.0.1 - 2021-06-16
 
 ### Fixed
diff --git a/hercules-ci-cnix-store.cabal b/hercules-ci-cnix-store.cabal
--- a/hercules-ci-cnix-store.cabal
+++ b/hercules-ci-cnix-store.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.4
 
 name:           hercules-ci-cnix-store
-version:        0.2.0.1
+version:        0.2.1.0
 synopsis:       Haskell bindings for Nix's libstore
 category:       Nix
 homepage:       https://docs.hercules-ci.com
@@ -103,6 +103,7 @@
     , containers
     , protolude
     , template-haskell
+    , unix
     , unliftio-core
     , vector
   extra-libraries:
diff --git a/src/Hercules/CNix.hs b/src/Hercules/CNix.hs
--- a/src/Hercules/CNix.hs
+++ b/src/Hercules/CNix.hs
@@ -106,3 +106,4 @@
       strdup(nix::nixVersion.c_str())
     }|]
   unsafePackMallocCString p
+{-# NOINLINE nixVersion #-}
diff --git a/src/Hercules/CNix/Store.hs b/src/Hercules/CNix/Store.hs
--- a/src/Hercules/CNix/Store.hs
+++ b/src/Hercules/CNix/Store.hs
@@ -287,6 +287,7 @@
 ensurePath :: Store -> StorePath -> IO ()
 ensurePath (Store store) (StorePath storePath) =
   [C.throwBlock| void {
+    ReceiveInterrupts _;
     Store &store = **$(refStore* store);
     StorePath &storePath = *$fptr-ptr:(nix::StorePath *storePath);
     store.ensurePath(printPath23(store, storePath));
@@ -353,6 +354,7 @@
 buildPaths :: Store -> StdVector C.NixStorePathWithOutputs -> IO ()
 buildPaths (Store store) (StdVector paths) = do
   [C.throwBlock| void {
+    ReceiveInterrupts _;
     Store &store = **$(refStore* store);
     std::vector<StorePathWithOutputs> &paths = *$fptr-ptr:(std::vector<nix::StorePathWithOutputs>* paths);
     store.buildPaths(toDerivedPaths24(printPathSet23(store, paths)));
@@ -382,6 +384,7 @@
 getDerivation (Store store) (StorePath spwo) = do
   moveToForeignPtrWrapper
     =<< [C.throwBlock| Derivation *{
+      ReceiveInterrupts _;
       Store &store = **$(refStore* store);
       return new Derivation(
           store.derivationFromPath(printPath23(store, *$fptr-ptr:(nix::StorePath *spwo)))
@@ -786,6 +789,7 @@
   (StdVector pathsVector') <- Std.Vector.fromList (pathList <&> \(StorePath c) -> unsafeForeignPtrToPtr c)
   withForeignPtr pathsVector' \pathsVector ->
     [C.throwBlock| void {
+      ReceiveInterrupts _;
       ref<Store> src = *$(refStore* src);
       ref<Store> dest = *$(refStore* dest);
       std::vector<nix::StorePath *> &pathsVector = *$(std::vector<nix::StorePath*>* pathsVector);
@@ -797,7 +801,11 @@
       StorePathSet closurePaths;
       compatComputeFSClosure(*src, pathSet, closurePaths);
 
+#ifdef NIX_2_4
+      nix::copyPaths(*src, *dest, closurePaths);
+#else
       nix::copyPaths(src, dest, compatPathSet(*src, closurePaths));
+#endif
     }|]
   for_ pathList (\(StorePath c) -> touchForeignPtr c)
 
@@ -830,6 +838,7 @@
 signPath (Store store) secretKey (StorePath path) =
   (== 1) <$> do
     [C.throwBlock| int {
+    ReceiveInterrupts _;
     nix::ref<nix::Store> store = *$(refStore *store);
     const StorePath &storePath = *$fptr-ptr:(nix::StorePath *path);
     const SecretKey &secretKey = *$(SecretKey *secretKey);
@@ -858,6 +867,7 @@
 followLinksToStorePath (Store store) bs =
   moveStorePath
     =<< [C.throwBlock| nix::StorePath *{
+      ReceiveInterrupts _;
       Store &store = **$(refStore* store);
       std::string s = std::string($bs-ptr:bs, $bs-len:bs);
       return new StorePath(parseStorePath23(store, store.followLinksToStorePath(s)));
@@ -872,6 +882,7 @@
 queryPathInfo (Store store) (StorePath path) = do
   vpi <-
     [C.throwBlock| refValidPathInfo* {
+      ReceiveInterrupts _;
       Store &store = **$(refStore* store);
       StorePath &path = *$fptr-ptr:(nix::StorePath *path);
       return new refValidPathInfo(store.queryPathInfo(printPath23(store, path)));
@@ -957,6 +968,7 @@
       inclDrv = countTrue $ includeDerivers params
   ret@(Std.Set.StdSet retSet) <- Std.Set.new
   [C.throwBlock| void {
+    ReceiveInterrupts _;
     Store &store = **$(refStore* store);
     StorePathSet &ret = *$fptr-ptr:(std::set<nix::StorePath>* retSet);
     compatComputeFSClosure(store, *$fptr-ptr:(std::set<nix::StorePath>* startingSet), ret,
diff --git a/src/Hercules/CNix/Util.hs b/src/Hercules/CNix/Util.hs
--- a/src/Hercules/CNix/Util.hs
+++ b/src/Hercules/CNix/Util.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell #-}
@@ -7,6 +8,8 @@
 module Hercules.CNix.Util
   ( setInterruptThrown,
     triggerInterrupt,
+    installDefaultSigINTHandler,
+    createInterruptCallback,
   )
 where
 
@@ -16,6 +19,8 @@
 import qualified Language.C.Inline.Cpp as C
 import qualified Language.C.Inline.Cpp.Exceptions as C
 import Protolude
+import System.Mem.Weak (deRefWeak)
+import System.Posix (Handler (Catch), installHandler, sigHUP, sigINT, sigTERM, sigUSR1)
 import Prelude ()
 
 C.context context
@@ -39,3 +44,53 @@
   [C.throwBlock| void {
     nix::triggerInterrupt();
   } |]
+
+installDefaultSigINTHandler :: IO ()
+installDefaultSigINTHandler = do
+  mainThread <- myThreadId
+  weakId <- mkWeakThreadId mainThread
+  let defaultHaskellHandler = do
+        mt <- deRefWeak weakId
+        for_ mt \t -> do
+          throwTo t (toException UserInterrupt)
+
+  -- Install Nix interrupter in Haskell
+  _oldHandler <-
+    for [sigINT, sigTERM, sigHUP] \sig ->
+      installHandler
+        sig
+        ( Catch do
+            triggerInterrupt
+            defaultHaskellHandler
+        )
+        Nothing
+
+  -- Install dummy SIGUSR1 handler for Nix interrupt signal propagation
+  -- (installHandler uses process-wide sigprocmask, so this should apply to all
+  -- capability threads, as required for Nix)
+  _oldHandler <-
+    installHandler
+      sigUSR1
+      ( -- Not Ignore, because we want to cause EINTR
+        Catch pass
+      )
+      Nothing
+
+  -- Install Haskell interrupter in Nix
+  createInterruptCallback defaultHaskellHandler
+
+createInterruptCallback :: IO () -> IO ()
+createInterruptCallback onInterrupt = do
+  onInterruptPtr <- mkCallback onInterrupt
+  -- leaks onInterruptPtr
+  [C.throwBlock| void {
+    nix::createInterruptCallback($(void (*onInterruptPtr)()));
+  } |]
+
+#ifndef __GHCIDE__
+foreign import ccall "wrapper"
+  mkCallback :: IO () -> IO (FunPtr (IO ()))
+#else
+mkCallback :: IO () -> IO (FunPtr (IO ()))
+mkCallback = panic "This is a stub to work around a ghcide issue. Please compile without -D__GHCIDE__"
+#endif
