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](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [1.7.6] - 2025-01-23
+
+### Fixed
+
+- cachix use: support `include` statements when adding caches to `nix.conf`
+
 ## [1.7.5] - 2024-10-11
 
 ### Fixed
diff --git a/cachix.cabal b/cachix.cabal
--- a/cachix.cabal
+++ b/cachix.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               cachix
-version:            1.7.5
+version:            1.7.6
 synopsis:
   Command-line client for Nix binary cache hosting https://cachix.org
 
@@ -263,6 +263,7 @@
     , dhall
     , directory
     , extra
+    , filepath
     , hercules-ci-cnix-store
     , here
     , hspec
diff --git a/src/Cachix/Client/Exception.hs b/src/Cachix/Client/Exception.hs
--- a/src/Cachix/Client/Exception.hs
+++ b/src/Cachix/Client/Exception.hs
@@ -12,6 +12,8 @@
   | NoInput Text
   | NoConfig Text
   | NetRcParseError Text
+  | IncludeNotFound Text
+  | CircularInclude Text
   | NarStreamingError ExitCode Text
   | NarHashMismatch Text
   | DeprecatedCommand Text
@@ -33,6 +35,8 @@
   displayException (ArtifactNotFound s) = toS s
   displayException (NoSigningKey s) = toS s
   displayException (NetRcParseError s) = toS s
+  displayException (IncludeNotFound s) = toS s
+  displayException (CircularInclude s) = toS s
   displayException (NarStreamingError _ s) = toS s
   displayException (NarHashMismatch s) = toS s
   displayException (DeprecatedCommand s) = toS s
diff --git a/src/Cachix/Client/InstallationMode.hs b/src/Cachix/Client/InstallationMode.hs
--- a/src/Cachix/Client/InstallationMode.hs
+++ b/src/Cachix/Client/InstallationMode.hs
@@ -81,8 +81,8 @@
 getNixEnv :: IO NixEnv
 getNixEnv = do
   user <- getUser
-  nc <- NixConf.read NixConf.Global
-  isTrusted <- isTrustedUser $ NixConf.readLines (catMaybes [nc]) NixConf.isTrustedUsers
+  ncs <- NixConf.resolveIncludes =<< NixConf.readWithDefault NixConf.Global
+  isTrusted <- isTrustedUser $ concatMap (NixConf.readLines NixConf.isTrustedUsers) ncs
   isNixOS <- doesFileExist "/run/current-system/nixos-version"
   return $
     NixEnv
@@ -139,37 +139,55 @@
 addBinaryCache config bc _ (Install ncl) = do
   (input, output) <- prepareNixConf ncl
   netrcLocMaybe <- forM (guard $ not (BinaryCache.isPublic bc)) $ const $ addPrivateBinaryCacheNetRC config bc ncl
-  let addNetRCLine :: NixConf.NixConf -> NixConf.NixConf
+  let addNetRCLine :: NixConf.NixConfSource -> NixConf.NixConfSource
       addNetRCLine = fromMaybe identity $ do
         netrcLoc <- netrcLocMaybe :: Maybe FilePath
         -- We only add the netrc line for local user configs for now.
         -- On NixOS we assume it will be picked up from the default location.
         guard (ncl == NixConf.Local)
-        pure (NixConf.setNetRC $ toS netrcLoc)
-  NixConf.write ncl $ addNetRCLine $ NixConf.add bc input output
+        pure $ setNetRC (toS netrcLoc)
+  NixConf.write $ addNetRCLine $ NixConf.add bc input output
   filename <- NixConf.getFilename ncl
   putStrLn $ "Configured " <> BinaryCache.uri bc <> " binary cache in " <> toS filename
 
-prepareNixConf :: NixConf.NixConfLoc -> IO ([NixConf.NixConf], NixConf.NixConf)
+-- | Resolve and read the nix.conf.
+--
+-- Returns a set of "input" confs and the "output" conf.
+--
+-- The output is the parsed conf file at the location specified by the NixConfLoc.
+--
+-- Inputs are any confs included by the output conf, plus any additional external resolutions.
+-- For example, for the local Nix conf, we also return the global one.
+prepareNixConf :: NixConf.NixConfLoc -> IO ([NixConf.NixConfSource], NixConf.NixConfSource)
 prepareNixConf ncl = do
+  outputPath <- NixConf.getFilename ncl
   -- TODO: might need locking one day
   gnc <- NixConf.read NixConf.Global
+  gncInputs <- traverse NixConf.resolveIncludes gnc
+
   (input, output) <-
     case ncl of
-      NixConf.Global -> return ([gnc], gnc)
+      NixConf.Global -> do
+        return (gncInputs, gnc)
       NixConf.Local -> do
         lnc <- NixConf.read NixConf.Local
-        return ([gnc, lnc], lnc)
+        lncInputs <- traverse NixConf.resolveIncludes lnc
+        return (gncInputs <> lncInputs, lnc)
       NixConf.Custom _ -> do
         lnc <- NixConf.read ncl
-        return ([lnc], lnc)
-  return (catMaybes input, fromMaybe (NixConf.NixConf []) output)
+        lncInputs <- traverse NixConf.resolveIncludes lnc
+        return (lncInputs, lnc)
 
+  return
+    ( fromMaybe [] input,
+      fromMaybe (NixConf.new outputPath) output
+    )
+
 removeBinaryCache :: URI.URI -> Text -> InstallationMode -> IO ()
 removeBinaryCache uri name (Install ncl) = do
-  contents <- fromMaybe (NixConf.NixConf []) <$> NixConf.read ncl
+  contents <- NixConf.readWithDefault ncl
   let (final, removed) = NixConf.remove uri name [contents] contents
-  NixConf.write ncl final
+  NixConf.write final
   filename <- NixConf.getFilename ncl
   if removed
     then putStrLn $ "Removed " <> host <> " binary cache in " <> toS filename
@@ -178,6 +196,12 @@
     host = URI.toByteString (URI.appendSubdomain name uri)
 removeBinaryCache _ _ _ = do
   throwIO $ RemoveCacheUnsupported "Removing binary caches is only supported for nix.conf"
+
+setNetRC :: Text -> NixConf.NixConfSource -> NixConf.NixConfSource
+setNetRC netrc conf = (fmap . fmap) (\ls -> filter noNetRc ls ++ [NixConf.NetRcFile netrc]) conf
+  where
+    noNetRc (NixConf.NetRcFile _) = False
+    noNetRc _ = True
 
 nixosBinaryCache :: Config -> BinaryCache.BinaryCache -> UseOptions -> IO ()
 nixosBinaryCache config bc UseOptions {useNixOSFolder = baseDirectory} = do
diff --git a/src/Cachix/Client/NixConf.hs b/src/Cachix/Client/NixConf.hs
--- a/src/Cachix/Client/NixConf.hs
+++ b/src/Cachix/Client/NixConf.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE DeriveFunctor #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 
-{- (Very limited) parser, rendered and modifier of nix.conf
+{- (Very limited) parser, renderer and modifier of nix.conf
 
 Supports subset of nix.conf given Nix 2.0 or Nix 1.0
 
@@ -14,12 +13,17 @@
   ( NixConf,
     NixConfG (..),
     NixConfLine (..),
+    NixConfSource,
+    NixConfSourceG (..),
     NixConfLoc (..),
+    IncludeType (..),
+    new,
     render,
     add,
     remove,
     read,
-    update,
+    readWithDefault,
+    resolveIncludes,
     write,
     getFilename,
     parser,
@@ -29,10 +33,10 @@
     isTrustedUsers,
     defaultPublicURI,
     defaultSigningKey,
-    setNetRC,
   )
 where
 
+import Cachix.Client.Exception (CachixException (..))
 import Cachix.Client.URI qualified as URI
 import Cachix.Types.BinaryCache qualified as BinaryCache
 import Data.List (nub)
@@ -45,35 +49,114 @@
     doesFileExist,
     getXdgDirectory,
   )
-import System.FilePath.Posix (takeDirectory)
+import System.FilePath (normalise)
+import System.FilePath.Posix (takeDirectory, (</>))
 import Text.Megaparsec qualified as Mega
 import Text.Megaparsec.Char
 
+defaultPublicURI :: Text
+defaultPublicURI = "https://cache.nixos.org"
+
+defaultSigningKey :: Text
+defaultSigningKey = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
+
 data NixConfLine
   = Substituters [Text]
   | TrustedUsers [Text]
   | TrustedPublicKeys [Text]
   | NetRcFile Text
+  | Include IncludeType
   | Other Text
   deriving (Show, Eq)
 
-newtype NixConfG a = NixConf a
-  deriving (Show, Eq, Functor)
+data IncludeType
+  = RequiredInclude Text -- for "include"
+  | OptionalInclude Text -- for "!include"
+  deriving (Show, Eq)
 
+-- | A list of conf lines
 type NixConf = NixConfG [NixConfLine]
 
-readLines :: [NixConf] -> (NixConfLine -> Maybe [Text]) -> [Text]
-readLines nixconfs predicate = concatMap f nixconfs
-  where
-    f (NixConf xs) = foldl foldIt [] xs
-    foldIt :: [Text] -> NixConfLine -> [Text]
-    foldIt prev new = prev <> fromMaybe [] (predicate new)
+newtype NixConfG a = NixConf a
+  deriving stock (Show, Eq, Functor)
 
-writeLines :: (NixConfLine -> Maybe [Text]) -> NixConfLine -> NixConf -> NixConf
-writeLines predicate addition = fmap f
-  where
-    f x = filter (isNothing . predicate) x <> [addition]
+-- | A wrapper around NixConf that also tracks the path to the nix.conf file
+type NixConfSource = NixConfSourceG NixConf
 
+data NixConfSourceG a = NixConfSource
+  { nixConfPath :: FilePath,
+    nixConfLines :: a
+  }
+  deriving stock (Show, Eq, Functor)
+
+-- | Operations on nix.conf.
+-- Helps to work with both NixConf and NixConfSource.
+class NixConfOps a where
+  -- | Read the nix.conf lines that match the given predicate
+  readLines :: (NixConfLine -> Maybe [Text]) -> a -> [Text]
+
+  -- | Write the given lines to the nix.conf
+  writeLines :: (NixConfLine -> Maybe [Text]) -> NixConfLine -> a -> a
+
+  -- | Add the given binary cache to the nix.conf
+  add :: BinaryCache.BinaryCache -> [a] -> a -> a
+
+  -- | Remove the given binary cache from the nix.conf
+  remove :: URI.URI -> Text -> [a] -> a -> (a, Bool)
+
+  -- | Render the nix.conf to a Text
+  render :: a -> Text
+
+instance NixConfOps NixConf where
+  readLines predicate (NixConf xs) = foldl f [] xs
+    where
+      f :: [Text] -> NixConfLine -> [Text]
+      f prev next = prev <> fromMaybe [] (predicate next)
+
+  writeLines predicate addition = fmap f
+    where
+      f x = filter (isNothing . predicate) x <> [addition]
+
+  add bc toRead toWrite =
+    writeLines isPublicKey (TrustedPublicKeys $ nub publicKeys) $
+      writeLines isSubstituter (Substituters $ nub substituters) toWrite
+    where
+      -- Note: some defaults are always appended since overriding some setttings in nix.conf overrides defaults otherwise
+      substituters = (defaultPublicURI : concatMap (readLines isSubstituter) toRead) <> [BinaryCache.uri bc]
+      publicKeys = (defaultSigningKey : concatMap (readLines isPublicKey) toRead) <> BinaryCache.publicSigningKeys bc
+
+  remove uri name toRead toWrite =
+    (newconf, oldsubstituters /= substituters)
+    where
+      newconf =
+        writeLines isPublicKey (TrustedPublicKeys $ nub publicKeys) $
+          writeLines isSubstituter (Substituters $ nub substituters) toWrite
+      oldsubstituters = concatMap (readLines isSubstituter) toRead
+      substituters = filter (toS (URI.toByteString fulluri) /=) oldsubstituters
+      oldpublicKeys = concatMap (readLines isPublicKey) toRead
+      publicKeys = filter (not . T.isPrefixOf (toS $ URI.hostBS $ URI.getHostname fulluri)) oldpublicKeys
+      fulluri = URI.appendSubdomain name uri
+
+  render (NixConf ls) = T.unlines $ fmap go ls
+    where
+      go :: NixConfLine -> Text
+      go (Substituters xs) = "substituters" <> " = " <> T.unwords xs
+      go (TrustedUsers xs) = "trusted-users = " <> T.unwords xs
+      go (TrustedPublicKeys xs) = "trusted-public-keys" <> " = " <> T.unwords xs
+      go (NetRcFile filename) = "netrc-file = " <> filename
+      go (Include (RequiredInclude path)) = "include " <> path
+      go (Include (OptionalInclude path)) = "!include " <> path
+      go (Other line) = line
+
+instance NixConfOps NixConfSource where
+  readLines f = readLines f . nixConfLines
+  writeLines f = fmap . writeLines f
+  add bc toRead toWrite = toWrite {nixConfLines = add bc (fmap nixConfLines toRead) (nixConfLines toWrite)}
+  remove uri name toRead toWrite =
+    let (newLines, changed) = remove uri name (fmap nixConfLines toRead) (nixConfLines toWrite)
+     in (toWrite {nixConfLines = newLines}, changed)
+  render = render . nixConfLines
+
 isSubstituter :: NixConfLine -> Maybe [Text]
 isSubstituter (Substituters xs) = Just xs
 isSubstituter _ = Nothing
@@ -86,54 +169,75 @@
 isTrustedUsers (TrustedUsers xs) = Just xs
 isTrustedUsers _ = Nothing
 
--- | Pure version of addIO
-add :: BinaryCache.BinaryCache -> [NixConf] -> NixConf -> NixConf
-add bc toRead toWrite =
-  writeLines isPublicKey (TrustedPublicKeys $ nub publicKeys) $
-    writeLines isSubstituter (Substituters $ nub substituters) toWrite
-  where
-    -- Note: some defaults are always appended since overriding some setttings in nix.conf overrides defaults otherwise
-    substituters = (defaultPublicURI : readLines toRead isSubstituter) <> [BinaryCache.uri bc]
-    publicKeys = (defaultSigningKey : readLines toRead isPublicKey) <> BinaryCache.publicSigningKeys bc
+-- | Create a new, empty NixConfSource with the given path
+new :: FilePath -> NixConfSource
+new path = NixConfSource path (NixConf [])
 
-remove :: URI.URI -> Text -> [NixConf] -> NixConf -> (NixConf, Bool)
-remove uri name toRead toWrite =
-  (newconf, oldsubstituters /= substituters)
+write :: NixConfSource -> IO ()
+write NixConfSource {nixConfPath, nixConfLines} = do
+  createDirectoryIfMissing True (takeDirectory nixConfPath)
+  writeFile nixConfPath $ render nixConfLines
+
+-- | Resolves includes in the given NixConfSource, starting from the given source file.
+resolveIncludes :: NixConfSource -> IO [NixConfSource]
+resolveIncludes conf@NixConfSource {nixConfPath} =
+  resolveIncludesWithStack [normalise nixConfPath] nixConfPath conf
+
+resolveIncludesWithStack :: [FilePath] -> FilePath -> NixConfSource -> IO [NixConfSource]
+resolveIncludesWithStack stack baseFile baseConf@(NixConfSource {nixConfLines = NixConf ls}) = do
+  includedConfigs <- mapM resolveInclude [f | Include f <- ls]
+  return $ baseConf : concat includedConfigs
   where
-    newconf =
-      writeLines isPublicKey (TrustedPublicKeys $ nub publicKeys) $
-        writeLines isSubstituter (Substituters $ nub substituters) toWrite
-    oldsubstituters = readLines toRead isSubstituter
-    substituters = filter (toS (URI.toByteString fulluri) /=) oldsubstituters
-    oldpublicKeys = readLines toRead isPublicKey
-    publicKeys = filter (not . T.isPrefixOf (toS $ URI.hostBS $ URI.getHostname fulluri)) oldpublicKeys
-    fulluri = URI.appendSubdomain name uri
+    dir = takeDirectory baseFile
 
-defaultPublicURI :: Text
-defaultPublicURI = "https://cache.nixos.org"
+    resolveInclude :: IncludeType -> IO [NixConfSource]
+    resolveInclude includeType = do
+      let path = case includeType of
+            RequiredInclude p -> p
+            OptionalInclude p -> p
+          fullPath = normalise $ dir </> toS path
 
-defaultSigningKey :: Text
-defaultSigningKey = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
+      if fullPath `elem` stack
+        then case includeType of
+          RequiredInclude _ -> throwIO $ CircularInclude (formatCircularError fullPath)
+          OptionalInclude _ -> return []
+        else do
+          exists <- doesFileExist fullPath
+          if not exists && isRequired includeType
+            then throwIO $ IncludeNotFound $ toS fullPath
+            else do
+              content <- readFile fullPath
+              case parse content of
+                Left _err -> return []
+                Right conf -> resolveIncludesWithStack (fullPath : stack) baseFile (NixConfSource fullPath conf)
 
-render :: NixConf -> Text
-render (NixConf nixconflines) = T.unlines $ fmap go nixconflines
-  where
-    go :: NixConfLine -> Text
-    go (Substituters xs) = "substituters" <> " = " <> T.unwords xs
-    go (TrustedUsers xs) = "trusted-users = " <> T.unwords xs
-    go (TrustedPublicKeys xs) = "trusted-public-keys" <> " = " <> T.unwords xs
-    go (NetRcFile filename) = "netrc-file = " <> filename
-    go (Other line) = line
+    isRequired (RequiredInclude _) = True
+    isRequired (OptionalInclude _) = False
 
-write :: NixConfLoc -> NixConf -> IO ()
-write ncl nc = do
-  filename <- getFilename ncl
-  createDirectoryIfMissing True (takeDirectory filename)
-  writeFile filename $ render nc
+    formatCircularError path =
+      "Circular include detected:\n" <> T.intercalate "\n" (formatChain (reverse stack) path)
 
-read :: NixConfLoc -> IO (Maybe NixConf)
+    formatChain :: [FilePath] -> FilePath -> [Text]
+    formatChain chain target =
+      case chain of
+        [] -> []
+        (p : ps) ->
+          format p
+            : map (("    -> includes " <>) . format) ps
+            ++ ["    -> includes " <> format target <> " (circular reference)\n"]
+      where
+        format = toS . normalise :: FilePath -> Text
+
+data NixConfLoc = Global | Local | Custom FilePath
+  deriving stock (Show, Eq)
+
+read :: NixConfLoc -> IO (Maybe NixConfSource)
 read ncl = do
   filename <- getFilename ncl
+  read' filename
+
+read' :: FilePath -> IO (Maybe NixConfSource)
+read' filename = do
   doesExist <- doesFileExist filename
   if not doesExist
     then return Nothing
@@ -143,21 +247,12 @@
         Left err -> do
           putStrLn (Mega.errorBundlePretty err)
           panic $ toS filename <> " failed to parse, please copy the above error and contents of nix.conf and open an issue at https://github.com/cachix/cachix"
-        Right conf -> return $ Just conf
-
-update :: NixConfLoc -> (Maybe NixConf -> NixConf) -> IO ()
-update ncl f = do
-  nc <- f <$> read ncl
-  write ncl nc
-
-setNetRC :: Text -> NixConf -> NixConf
-setNetRC netrc (NixConf nc) = NixConf $ filter noNetRc nc ++ [NetRcFile netrc]
-  where
-    noNetRc (NetRcFile _) = False
-    noNetRc _ = True
+        Right conf -> return (Just (NixConfSource filename conf))
 
-data NixConfLoc = Global | Local | Custom FilePath
-  deriving (Show, Eq)
+readWithDefault :: NixConfLoc -> IO NixConfSource
+readWithDefault ncl = do
+  filename <- getFilename ncl
+  fromMaybe (new filename) <$> read' filename
 
 getFilename :: NixConfLoc -> IO FilePath
 getFilename ncl = do
@@ -183,6 +278,15 @@
   _ <- many spaceChar
   return $ constr (fmap toS values)
 
+parseInclude :: (Text -> IncludeType) -> Text -> Parser NixConfLine
+parseInclude constr name = Mega.try $ do
+  _ <- optional (some (char ' '))
+  _ <- string name
+  _ <- some (char ' ')
+  path <- many (Mega.satisfy (not . isSpace))
+  _ <- many spaceChar
+  return $ Include (constr (toS path))
+
 parseOther :: Parser NixConfLine
 parseOther = Mega.try $ Other . toS <$> Mega.someTill Mega.anySingle (void eol <|> Mega.eof)
 
@@ -196,6 +300,8 @@
     <|> parseLine Substituters "binary-caches"
     -- NB: assume that space in this option means space in filename
     <|> parseLine (NetRcFile . T.concat) "netrc-file"
+    <|> parseInclude RequiredInclude "include"
+    <|> parseInclude OptionalInclude "!include"
     <|> parseOther
 
 parser :: Parser NixConf
diff --git a/test/NixConfSpec.hs b/test/NixConfSpec.hs
--- a/test/NixConfSpec.hs
+++ b/test/NixConfSpec.hs
@@ -2,11 +2,14 @@
 
 module NixConfSpec where
 
+import Cachix.Client.Exception (CachixException (CircularInclude))
 import Cachix.Client.NixConf as NixConf
 import Cachix.Types.BinaryCache (BinaryCache (..), CompressionMethod (..))
 import Cachix.Types.Permission (Permission (..))
 import Data.String.Here
 import Protolude
+import System.FilePath ((</>))
+import System.IO.Temp (withTempDirectory)
 import Test.Hspec
 
 property :: Text -> Expectation
@@ -33,8 +36,11 @@
       property "substituters = a b c\n"
     it "handles all known keys" $
       property "substituters = a b c\ntrusted-users = him me\ntrusted-public-keys = a\n"
+    it "handles includes" $
+      property "include /etc/nix/nix.conf\n!include /etc/nix/nix.conf\n"
     it "random content" $
       property "blabla = foobar\nfoo = bar\n"
+
   describe "add" $ do
     it "merges binary caches from both files" $
       let globalConf =
@@ -53,6 +59,7 @@
                 TrustedPublicKeys [defaultSigningKey, "pub1", "pub2", "pub"]
               ]
        in add bc [globalConf, localConf] localConf `shouldBe` result
+
     it "is noop if binary cache exists in one file" $
       let globalConf =
             NixConf
@@ -66,6 +73,7 @@
                 TrustedPublicKeys [defaultSigningKey, "pub"]
               ]
        in add bc [globalConf, localConf] localConf `shouldBe` result
+
     it "preserves other nixconf entries" $
       let globalConf =
             NixConf
@@ -87,6 +95,7 @@
                 TrustedPublicKeys [defaultSigningKey, "pub1", "pub"]
               ]
        in add bc [globalConf, localConf] localConf `shouldBe` result
+
     it "removed duplicates" $
       let globalConf =
             NixConf
@@ -104,6 +113,7 @@
                 TrustedPublicKeys [defaultSigningKey, "pub1", "pub2", "pub"]
               ]
        in add bc [globalConf, localConf] localConf `shouldBe` result
+
     it "adds binary cache and defaults if no existing entries exist" $
       let globalConf = NixConf []
           localConf = NixConf []
@@ -113,6 +123,7 @@
                 TrustedPublicKeys [defaultSigningKey, "pub"]
               ]
        in add bc [globalConf, localConf] localConf `shouldBe` result
+
   describe "remove" $ do
     it "removes a binary cache" $
       let localConf =
@@ -126,6 +137,7 @@
                 TrustedPublicKeys [defaultSigningKey]
               ]
        in remove "https://cachix.org" "name" [localConf] localConf `shouldBe` (result, True)
+
     it "removes nothing if the binary cache is missing" $
       let globalConf =
             NixConf
@@ -135,37 +147,99 @@
           localConf = globalConf
           result = localConf
        in remove "https://cachix.org" "name" [globalConf, localConf] localConf `shouldBe` (result, False)
+
   describe "parse" $ do
     it "parses substituters" $
       parse "substituters = a\n"
         `shouldBe` Right (NixConf [Substituters ["a"]])
+
     it "parses long key" $
       parse "binary-caches-parallel-connections = 40\n"
         `shouldBe` Right (NixConf [Other "binary-caches-parallel-connections = 40"])
+
     it "parses substituters with multiple values" $
       parse "substituters = a b c\n"
         `shouldBe` Right (NixConf [Substituters ["a", "b", "c"]])
+
     it "parses equal sign after the first key as literal" $
       parse "substituters = a b c= d\n"
         `shouldBe` Right (NixConf [Substituters ["a", "b", "c=", "d"]])
+
     it "parses with missing endline" $
       parse "allowed-users = *"
         `shouldBe` Right (NixConf [Other "allowed-users = *"])
+
+    it "parses include" $
+      parse "include /etc/nix/nix.conf\n"
+        `shouldBe` Right (NixConf [Include (RequiredInclude "/etc/nix/nix.conf")])
+
+    it "parses !include" $
+      parse "!include /etc/nix/nix.conf\n"
+        `shouldBe` Right (NixConf [Include (OptionalInclude "/etc/nix/nix.conf")])
+
     it "parses a complex example" $
       parse realExample
-        `shouldBe` Right
-          ( NixConf
-              [ Other "",
-                Substituters ["a", "b", "c"],
-                TrustedUsers ["him", "me"],
-                TrustedPublicKeys ["a"],
-                Other "blabla =  asd",
-                Other "# comment",
-                Other "",
-                Other ""
-              ]
-          )
+        `shouldBe` Right parsedRealExample
 
+  describe "NixConfSource" $ do
+    it "write . read" $ do
+      withTempDirectory "/tmp" "nixconf" $ \temp -> do
+        let confPath = temp </> "nix.conf"
+        let subConfPath = temp </> "sub.conf"
+        let confContents = "include " <> toS subConfPath <> "\n"
+        let parsedConfContents = NixConf [Include (RequiredInclude (toS subConfPath))]
+        writeFile confPath confContents
+        writeFile subConfPath realExample
+
+        Just conf <- NixConf.read (Custom temp)
+        conf `shouldBe` NixConfSource confPath parsedConfContents
+
+        NixConf.write conf
+        readFile confPath `shouldReturn` confContents
+
+    it "resolves includes" $ do
+      withTempDirectory "/tmp" "nixconf" $ \temp -> do
+        let confPath = temp </> "nix.conf"
+            subConfPath = temp </> "sub.conf"
+            confContents = "include " <> toS subConfPath <> "\n"
+            parsedConfContents = NixConf [Include (RequiredInclude (toS subConfPath))]
+        writeFile confPath confContents
+        writeFile subConfPath realExample
+
+        Just conf <- NixConf.read (Custom temp)
+        NixConf.resolveIncludes conf
+          `shouldReturn` [ NixConfSource confPath parsedConfContents,
+                           NixConfSource subConfPath parsedRealExample
+                         ]
+
+    it "detects cycles" $ do
+      withTempDirectory "/tmp" "nixconf" $ \temp -> do
+        let confPath = temp </> "nix.conf"
+            subConfPath = temp </> "sub.conf"
+            confContents :: Text = "include " <> toS subConfPath <> "\n"
+            subConfContents :: Text = "include " <> toS confPath <> "\n"
+        writeFile confPath confContents
+        writeFile subConfPath subConfContents
+
+        Just conf <- NixConf.read (Custom temp)
+
+        let isCircularInclude = \case
+              CircularInclude _ -> True
+              _ -> False
+        NixConf.resolveIncludes conf `shouldThrow` isCircularInclude
+
+    it "detects trusted-users through includes" $ do
+      withTempDirectory "/tmp" "nixconf" $ \temp -> do
+        let confPath = temp </> "nix.conf"
+            subConfPath = temp </> "sub.conf"
+            confContents :: Text = "include " <> toS subConfPath <> "\n"
+            subConfContents :: Text = "trusted-users = @wheel"
+        writeFile confPath confContents
+        writeFile subConfPath subConfContents
+
+        ncs <- NixConf.resolveIncludes =<< NixConf.readWithDefault (NixConf.Custom temp)
+        concatMap (NixConf.readLines NixConf.isTrustedUsers) ncs `shouldBe` ["@wheel"]
+
 realExample :: Text
 realExample =
   [hereLit|
@@ -177,3 +251,16 @@
 
 
 |]
+
+parsedRealExample :: NixConf
+parsedRealExample =
+  NixConf
+    [ Other "",
+      Substituters ["a", "b", "c"],
+      TrustedUsers ["him", "me"],
+      TrustedPublicKeys ["a"],
+      Other "blabla =  asd",
+      Other "# comment",
+      Other "",
+      Other ""
+    ]
