diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## 1.0.6
+### Changed
+- Support and require hnix-0.7.0 or higher.
+- Support GHC 8.10.1.
+- Use nixpkgs 20.03 in Nix builds.
+- Discard support for Nix versions < 1.12.
+
 ## 1.0.5
 ### Fixed
 - Add the golden test data to the source distribution by using `data-files`
diff --git a/hocker.cabal b/hocker.cabal
--- a/hocker.cabal
+++ b/hocker.cabal
@@ -1,5 +1,5 @@
 name:                hocker
-version:             1.0.5
+version:             1.0.6
 synopsis:            Interact with the docker registry and generate nix build instructions
 homepage:            https://github.com/awakesecurity/hocker#readme
 Bug-Reports:         https://github.com/awakesecurity/hocker/issues
@@ -68,8 +68,7 @@
                 base                 >= 4.9 && < 5,
                 aeson                >= 1.0.0.0,
                 aeson-pretty         >= 0.8,
-                ansi-wl-pprint       >= 0.6.7.3,
-                async                >= 2.0.0.0 && < 2.2,
+                async                >= 2.0.0.0 && < 2.3,
                 bytestring           >= 0.10,
                 concurrentoutput     >= 0.2,
                 containers           >= 0.5,
@@ -80,12 +79,13 @@
                 exceptions           >= 0.8,
                 filepath             >= 1.4,
                 foldl                >= 1.0,
-                hnix                 >= 0.3.4,
+                hnix                 >= 0.7.0,
                 http-client          >= 0.4,
                 http-types           >= 0.9.1,
                 lens                 >= 4.0,
                 lens-aeson           >= 1.0,
                 lifted-base          >= 0.2.3.8,
+                megaparsec           >= 7.0.0,
                 memory               >= 0.11,
                 mtl                  >= 2.2,
                 neat-interpolation   >= 0.3.2,
@@ -93,6 +93,7 @@
                 network-uri          >= 2.6,
                 optparse-applicative >= 0.13,
                 optparse-generic     >= 1.2.0,
+                prettyprinter        >= 1.1.1,
                 pooled-io            >= 0.0.2,
                 pureMD5              >= 2.1,
                 scientific           >= 0.3,
@@ -101,7 +102,7 @@
                 text                 >= 1.2,
                 time                 >= 1.4,
                 transformers         >= 0.4,
-                turtle               >= 1.3.0 && < 1.5,
+                turtle               >= 1.3.0 && < 1.6,
                 unordered-containers >= 0.2,
                 uri-bytestring       >= 0.2,
                 vector               >= 0.11,
@@ -201,7 +202,7 @@
                bytestring           >= 0.10,
                data-fix             >= 0.0.3,
                filepath             >= 1.4,
-               hnix                 >= 0.3.4,
+               hnix                 >= 0.5.2,
                hocker,
                lens                 >= 4.0,
                mtl                  >= 2.2,
@@ -225,7 +226,6 @@
   build-depends:
                 base                 >= 4.9 && < 5,
                 aeson                >= 0.9.0.1,
-                ansi-wl-pprint       >= 0.6.7.3,
                 bytestring           >= 0.10,
                 containers           >= 0.5,
                 cryptonite           >= 0.13,
@@ -233,13 +233,15 @@
                 mtl                  >= 2.2,
                 network              >= 2.6,
                 network-uri          >= 2.6,
+                prettyprinter        >= 1.1.1,
                 tasty                >= 0.11,
                 tasty-golden         >= 2.3,
                 tasty-hunit          >= 0.9,
                 tasty-quickcheck     >= 0.8,
                 tasty-smallcheck     >= 0.8,
                 text                 >= 1.2,
-                unordered-containers >= 0.2
+                unordered-containers >= 0.2,
+                word8                >= 0.1.0
 
 
   ghc-options:         -threaded -rtsopts -with-rtsopts=-N
diff --git a/src/Data/Docker/Nix/FetchDocker.hs b/src/Data/Docker/Nix/FetchDocker.hs
--- a/src/Data/Docker/Nix/FetchDocker.hs
+++ b/src/Data/Docker/Nix/FetchDocker.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP               #-}
 {-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE LambdaCase        #-}
 {-# LANGUAGE OverloadedStrings #-}
@@ -23,12 +24,13 @@
 import           Data.Coerce
 import           Data.Fix
 import           Data.Maybe
-import           Data.Monoid
+import           Data.Semigroup               ((<>))
 import           Data.Text                    (Text)
 import qualified Data.Text                    as Text
 import           Data.Text.Encoding           (decodeUtf8')
 import           Data.Text.Encoding.Error
-import           Nix.Expr
+import           Nix.Expr                     hiding (inherit)
+import qualified Nix.Expr                     (inherit)
 import           URI.ByteString
 
 import           Data.Docker.Image.Types
@@ -38,7 +40,21 @@
 import           Hocker.Types
 import           Hocker.Types.Exceptions
 import           Hocker.Types.ImageTag
+import           Text.Megaparsec.Pos          (Pos, mkPos)
 
+-- | @hnix-0.5.0:inherit@ requires a source location as its final argument.
+inheritAdapter :: FilePath -> Pos -> Pos -> [NKeyName e] -> Binding e
+inheritAdapter sourceName sourceLine sourceColumn ks = Nix.Expr.inherit ks
+#if MIN_VERSION_hnix(0,5,0)
+                                                         SourcePos{..}
+#endif
+
+-- | @hnix-0.5.0@ omits mkApp.
+#if MIN_VERSION_hnix(0,5,0)
+mkApp :: NExpr -> NExpr -> NExpr
+mkApp e = Fix . NBinary NApp e
+#endif
+
 {- Example output of the pretty-printed, generated Nix expression AST.
 { fetchdocker, fetchDockerConfig, fetchDockerLayer }:
 fetchdocker rec {
@@ -99,8 +115,6 @@
     pluckedConfigDigest = Hocker.Lib.stripHashId $ manifestJSON ^. key "config" . key "digest" . _String
     pluckedLayerDigests = Hocker.Lib.stripHashId <$> pluckLayersFrom manifestJSON
 
-
-
 {-| Generate a top-level Nix Expression AST from a 'HockerImageMeta'
 record, a config digest, and a list of layer digests.
 
@@ -134,19 +148,24 @@
         , StaticKey "imageName"
         ]
   let genLayerId i = mkSym . Text.pack $ "layer" <> show i
-  let fetchconfig = mkFetchDockerConfig (inherit $ ((StaticKey "tag"):commonInherits)) configDigest
+  let fetchconfig = mkFetchDockerConfig (inheritAdapter ("generated for " ++ show imageName) (mkPos 1) (mkPos 1) $ ((StaticKey "tag"):commonInherits)) configDigest
       fetchlayers =
         mkLets
-         (mkFetchDockerLayers (inherit commonInherits) layerDigests)
+         (mkFetchDockerLayers (inheritAdapter "common inherits" (mkPos 1) (mkPos 1) commonInherits) layerDigests)
          (mkList $ fmap genLayerId [0..(Prelude.length layerDigests)-1])
   fetchDockerExpr <- mkFetchDocker dim fetchconfig fetchlayers
   pure
     (mkFunction
       (mkParamset
-        [ ("fetchdocker",       Nothing)
-        , ("fetchDockerConfig", Nothing)
+        [ ("fetchDockerConfig", Nothing)
         , ("fetchDockerLayer",  Nothing)
-        ]) fetchDockerExpr)
+        , ("fetchdocker",       Nothing)
+        ] -- List keys in sorted order so that we do not care
+          -- whether hnix sorts keys or preserves this order.
+#if MIN_VERSION_hnix(0,5,0)
+        False  -- not variadic
+#endif
+      ) fetchDockerExpr)
 
 -- | Generate a @fetchdocker { ... }@ function call and argument
 -- attribute set. Please see 'generateFetchDockerExpr' documentation
@@ -155,7 +174,8 @@
 mkFetchDocker HockerImageMeta{..} fetchconfig fetchlayers = do
   registry <- Bifunctor.first mkHockerException serializedRegistry
   pure
-    (mkApp (mkSym constFetchdocker)
+    (mkSym constFetchdocker
+     @@
      (recAttrsE
       [ ("name",        mkStr $ fromMaybe imageName altImageName)
       , ("registry",    mkStr registry)
@@ -172,7 +192,6 @@
     mkHockerException err =
       HockerException (show err) Nothing Nothing
 
-
 -- | Generate a @fetchDockerConfig { ... }@ function call and
 -- argument attrset.
 --
@@ -181,8 +200,8 @@
 -- the output expression.
 mkFetchDockerConfig :: Binding NExpr -> Base32Digest -> NExpr
 mkFetchDockerConfig inherits (Base32Digest digest) =
-    mkApp (mkSym constFetchDockerConfig)
-          (Fix $ NSet [ inherits, "sha256" $= (mkStr digest) ])
+    mkSym constFetchDockerConfig @@
+          (Fix $ NSet NNonRecursive [ inherits, "sha256" $= (mkStr digest) ])
 
 -- | Generate a list of Nix expression ASTs representing
 -- @fetchDockerLayer { ... }@ function calls.
@@ -208,9 +227,9 @@
   where
     mkLayerId i = Text.pack $ "layer" <> show i
     mkFetchLayer (i, ((Base16Digest d16), (Base32Digest d32))) =
-      (mkLayerId i) $= mkApp (mkSym constFetchDockerLayer)
-                             (Fix $ NSet
+      (mkLayerId i) $= (mkSym constFetchDockerLayer @@
+                             (Fix $ NSet NNonRecursive
                                 [ inherits
                                 , "layerDigest" $= (mkStr d16) -- Required in order to perform a registry request
                                 , "sha256"      $= (mkStr d32) -- Required by Nix for fixed output derivations
-                                ])
+                                ]))
diff --git a/src/Data/Docker/Nix/Lib.hs b/src/Data/Docker/Nix/Lib.hs
--- a/src/Data/Docker/Nix/Lib.hs
+++ b/src/Data/Docker/Nix/Lib.hs
@@ -15,7 +15,7 @@
 
 module Data.Docker.Nix.Lib where
 
-import           Control.Foldl        as Foldl
+import qualified Control.Foldl        as Foldl
 import           Turtle
 import           Control.Monad.Except as Except
 import qualified Data.Text            as Text
diff --git a/src/Hocker/Lib.hs b/src/Hocker/Lib.hs
--- a/src/Hocker/Lib.hs
+++ b/src/Hocker/Lib.hs
@@ -28,9 +28,13 @@
 import qualified Data.ByteString.Char8        as C8
 import           Data.ByteString.Lazy.Char8   as C8L
 import           Data.Coerce
-import           Data.Monoid
+import           Data.Semigroup               ((<>))
 import           Data.Text                    (Text)
 import qualified Data.Text                    as Text
+import           Data.Text.Prettyprint.Doc    (LayoutOptions(..),
+                                               PageWidth(..), SimpleDocStream)
+import qualified Data.Text.Prettyprint.Doc
+import qualified Data.Text.Prettyprint.Doc.Render.Text
 import           Data.Text.Encoding           (encodeUtf8)
 import qualified Network.Wreq                 as Wreq
 import           Nix.Expr                     (NExpr)
@@ -39,9 +43,7 @@
 import           System.Environment           (getProgName)
 import           System.Exit                  as Exit
 import           System.FilePath.Posix        as File
-import           Text.PrettyPrint.ANSI.Leijen as Text.PrettyPrint (SimpleDoc,
-                                                                   displayS,
-                                                                   renderPretty)
+import qualified System.IO
 import           URI.ByteString
 
 import           Data.Docker.Image.Types
@@ -169,13 +171,19 @@
 splitRepository (ImageName (Text.pack -> n)) = over _2 Text.tail $ Text.break (=='/') n
 
 -- | Given a nix expression AST, produce a pretty printer document.
-renderNixExpr :: NExpr -> Text.PrettyPrint.SimpleDoc
-renderNixExpr = renderPretty 0.4 120 . prettyNix
+renderNixExpr :: NExpr -> SimpleDocStream ann
+renderNixExpr =
+    Data.Text.Prettyprint.Doc.layoutSmart layoutOptions . prettyNix
+  where
+    layoutOptions = LayoutOptions { layoutPageWidth = AvailablePerLine 120 0.4 }
 
 -- | Print a nix expression AST using the 'renderNixExpr' pretty
 -- printing renderer.
 pprintNixExpr :: NExpr -> IO ()
-pprintNixExpr expr = Prelude.putStrLn (displayS (renderNixExpr expr) "")
+pprintNixExpr expr =
+    Data.Text.Prettyprint.Doc.Render.Text.renderIO System.IO.stdout stream
+  where
+    stream = renderNixExpr expr
 
 -- | Given an executable's name, try to find it in the PATH.
 findExec :: (MonadIO m, Except.MonadError HockerException m)
diff --git a/src/Hocker/Types.hs b/src/Hocker/Types.hs
--- a/src/Hocker/Types.hs
+++ b/src/Hocker/Types.hs
@@ -31,7 +31,7 @@
 import qualified Crypto.Hash                as Hash
 import qualified Data.ByteString.Lazy
 import           Data.Char                  (toUpper)
-import           Data.Monoid
+import           Data.Semigroup             ((<>))
 import           Data.Text                  (Text)
 import qualified Data.Text                  as Text
 import qualified Network.Wreq               as Wreq
diff --git a/src/Hocker/Types/Exceptions.hs b/src/Hocker/Types/Exceptions.hs
--- a/src/Hocker/Types/Exceptions.hs
+++ b/src/Hocker/Types/Exceptions.hs
@@ -19,7 +19,7 @@
 
 import           Control.DeepSeq
 import           Control.Exception
-import           Data.Monoid
+import           Data.Semigroup     ((<>))
 import           GHC.Generics
 
 data HockerException = HockerException
diff --git a/src/Hocker/Types/Hash.hs b/src/Hocker/Types/Hash.hs
--- a/src/Hocker/Types/Hash.hs
+++ b/src/Hocker/Types/Hash.hs
@@ -17,7 +17,7 @@
 import qualified Data.ByteArray          as BA
 import qualified Data.ByteArray.Encoding as BA
 import qualified Data.ByteString.Char8   as C8
-import           Data.Monoid
+import           Data.Semigroup          ((<>))
 import qualified Data.Text
 import qualified Options.Applicative     as Options
 import           Options.Generic
diff --git a/src/Hocker/Types/ImageName.hs b/src/Hocker/Types/ImageName.hs
--- a/src/Hocker/Types/ImageName.hs
+++ b/src/Hocker/Types/ImageName.hs
@@ -15,7 +15,7 @@
 module Hocker.Types.ImageName where
 
 import           Control.DeepSeq
-import           Data.Monoid
+import           Data.Semigroup      ((<>))
 import qualified Options.Applicative as Options
 import           Options.Generic
 
diff --git a/src/Hocker/Types/ImageTag.hs b/src/Hocker/Types/ImageTag.hs
--- a/src/Hocker/Types/ImageTag.hs
+++ b/src/Hocker/Types/ImageTag.hs
@@ -15,7 +15,7 @@
 module Hocker.Types.ImageTag where
 
 import           Control.DeepSeq
-import           Data.Monoid
+import           Data.Semigroup      ((<>))
 import qualified Options.Applicative as Options
 import           Options.Generic
 
diff --git a/src/Hocker/Types/URI.hs b/src/Hocker/Types/URI.hs
--- a/src/Hocker/Types/URI.hs
+++ b/src/Hocker/Types/URI.hs
@@ -16,7 +16,7 @@
 
 import           Control.Lens
 import qualified Data.ByteString.Char8       as C8
-import           Data.Monoid
+import           Data.Semigroup              ((<>))
 import qualified Data.Text                   as Text
 import qualified Options.Applicative         as Options
 import           Options.Applicative.Builder
diff --git a/src/Network/Wreq/Docker/Image.hs b/src/Network/Wreq/Docker/Image.hs
--- a/src/Network/Wreq/Docker/Image.hs
+++ b/src/Network/Wreq/Docker/Image.hs
@@ -25,7 +25,7 @@
 import           Data.Coerce
 import           Data.Either
 import           Data.HashSet                  as Set
-import           Data.Monoid
+import           Data.Semigroup                ((<>))
 import           Data.Text                     (Text)
 import qualified Data.Text                     as Text
 import           Data.Text.Encoding            (decodeUtf8')
diff --git a/src/Network/Wreq/Docker/Image/Lib.hs b/src/Network/Wreq/Docker/Image/Lib.hs
--- a/src/Network/Wreq/Docker/Image/Lib.hs
+++ b/src/Network/Wreq/Docker/Image/Lib.hs
@@ -24,7 +24,7 @@
 import qualified Data.ByteString.Lazy.Char8        as C8L
 import           Data.Coerce
 import qualified Data.HashMap.Strict               as HashMap
-import           Data.Monoid
+import           Data.Semigroup                    ((<>))
 import qualified Data.Text                         as Text
 import qualified Network.Wreq                      as Wreq
 import qualified System.Directory                  as Directory
diff --git a/src/Network/Wreq/Docker/Registry.hs b/src/Network/Wreq/Docker/Registry.hs
--- a/src/Network/Wreq/Docker/Registry.hs
+++ b/src/Network/Wreq/Docker/Registry.hs
@@ -27,7 +27,6 @@
 import           Control.Lens
 import qualified Control.Monad.Except       as Except
 import           Control.Monad.Reader
-import           Data.Monoid
 import qualified Crypto.Hash                as Hash
 import           Data.Aeson.Lens
 import           Data.ByteString.Lazy.Char8 as C8L
@@ -35,6 +34,7 @@
 import           Data.Text.Encoding         (decodeUtf8, encodeUtf8)
 import           URI.ByteString
 import           NeatInterpolation
+import           Data.Semigroup             ((<>))
 import qualified Data.Text                  as Text
 import qualified Network.Wreq               as Wreq
 import           System.Directory
@@ -64,7 +64,7 @@
 --
 -- If 'Credentials' is either 'BearerToken' or 'Basic' then produce a
 -- 'Wreq.Auth' value for that type of credential.
--- 
+--
 -- If @Nothing@ is provided _and_ the provided 'RegistryURI' matches
 -- the default registry, make a request to
 -- @https://auth.docker.io/token@ for a temporary pull-only bearer
@@ -131,7 +131,7 @@
     mkURL (ImageName n) r = C8.unpack (serializeURIRef' $ Hocker.Lib.joinURIPath [n, "blobs", digest] r)
 
 -- | Retrieve a compressed layer blob by its hash digest.
--- 
+--
 -- TODO: take advantage of registry's support for the Range header so
 -- we can stream downloads.
 fetchLayer :: Layer -> Hocker RspBS
diff --git a/src/Network/Wreq/ErrorHandling.hs b/src/Network/Wreq/ErrorHandling.hs
--- a/src/Network/Wreq/ErrorHandling.hs
+++ b/src/Network/Wreq/ErrorHandling.hs
@@ -21,7 +21,7 @@
 import           Control.Lens
 import           Control.Monad.Except
 import           Data.ByteString.Char8     as C8
-import           Data.Monoid
+import           Data.Semigroup            ((<>))
 import           Network.HTTP.Client
 import           Network.HTTP.Types.Status
 
diff --git a/test/Tests/Data/Docker/Nix/FetchDocker.hs b/test/Tests/Data/Docker/Nix/FetchDocker.hs
--- a/test/Tests/Data/Docker/Nix/FetchDocker.hs
+++ b/test/Tests/Data/Docker/Nix/FetchDocker.hs
@@ -15,15 +15,19 @@
 
 import           Control.Exception            as CE
 import           Control.Monad.Except         as Except
+import           Data.ByteString              as BS
 import           Data.ByteString.Lazy.Char8   as C8L
 import           Data.Either                  (either)
 import qualified Data.Text                    as Text
+import qualified Data.Text.Prettyprint.Doc.Render.String
+import           Data.Word8                   as W8
 import           Network.URI
 
 import           Test.Tasty
 import           Test.Tasty.Golden
+import           Test.Tasty.Golden.Advanced   (goldenTest)
 import           Test.Tasty.HUnit
-import           Text.PrettyPrint.ANSI.Leijen as Text.PrettyPrint (displayS)
+import           Text.Printf                  (printf)
 
 import           Data.Docker.Image.Types
 import           Data.Docker.Nix.FetchDocker  as Nix.FetchDocker
@@ -33,8 +37,43 @@
 import           Hocker.Types
 import           Hocker.Types.ImageTag
 
+-- | Compare a given string against the golden file contents,
+-- ignoring differences in contiguous nonempty spans of whitespace,
+-- and the presence or absence of whitespace before or after a comma.
+goldenVsStringCanonicalize
+  :: TestName -- ^ test name
+  -> FilePath -- ^ path to golden file
+  -> IO C8L.ByteString -- ^ action that returns string to compare
+  -> TestTree -- ^ the test verifies that the returned string equals the
+              -- golden file contents when ignoring differences in whitespace
+goldenVsStringCanonicalize name ref act =
+  goldenTest
+    name
+    (BS.readFile ref)
+    (C8L.toStrict <$> act)
+    cmp
+    upd
+  where
+  cmp x y = cmpCanonicalize msg x y
+    where
+    msg = printf "Test output was different from '%s'. It was: %s" ref (show y)
+  upd = BS.writeFile ref
+
+cmpCanonicalize ::
+  String -> BS.ByteString -> BS.ByteString -> IO (Maybe String)
+cmpCanonicalize e x y =
+    return $ if canonicalize x == canonicalize y then Nothing else Just e
+  where
+    canonicalize = BS.pack . BS.foldr op []
+    op x acc
+      | W8.isSpace x, y : _ <- acc, y == W8._space = acc
+      | W8.isSpace x, y : _ <- acc, y == W8._comma = acc
+      | W8.isSpace x = W8._space : acc
+      | x == W8._comma, y : ys <- acc, y == W8._space = W8._comma : ys
+      | otherwise = x : acc
+
 tests = testGroup "FetchDocker Nix Generation Tests"
-  [ goldenVsString
+  [ goldenVsStringCanonicalize
       "Golden vs. Generated `fetchDocker' Nix Expression"
       "test/data/golden-debian_jessie.nix"
       generateFetchDockerNix
@@ -66,7 +105,9 @@
       , altImageName   = Nothing
       }
 
+  let display = Data.Text.Prettyprint.Doc.Render.String.renderString
+
   either
     (Hocker.Lib.die . Text.pack . show)
-    (return . C8L.pack . (flip displayS "") . Hocker.Lib.renderNixExpr)
+    (return . C8L.pack . display . Hocker.Lib.renderNixExpr)
     nixExpression
diff --git a/test/data/golden-debian_jessie.nix b/test/data/golden-debian_jessie.nix
--- a/test/data/golden-debian_jessie.nix
+++ b/test/data/golden-debian_jessie.nix
@@ -1,5 +1,7 @@
-{ fetchDockerConfig, fetchDockerLayer, fetchdocker }:
-fetchdocker rec {
+{ fetchDockerConfig
+  , fetchDockerLayer
+  , fetchdocker }:
+  fetchdocker rec {
     name = "debian";
     registry = "https://registry-1.docker.io/v2/";
     repository = "library";
@@ -8,12 +10,12 @@
     imageConfig = fetchDockerConfig {
       inherit tag registry repository imageName;
       sha256 = "1rwinmvfc8jxn54y7qnj82acrc97y7xcnn22zaz67y76n4wbwjh5";
-    };
+      };
     imageLayers = let
       layer0 = fetchDockerLayer {
         inherit registry repository imageName;
         layerDigest = "cd0a524342efac6edff500c17e625735bbe479c926439b263bbe3c8518a0849c";
         sha256 = "1744l0c8ag5y7ck9nhr6r5wy9frmaxi7xh80ypgnxb7g891m42nd";
-      };
-      in [ layer0 ];
-  }
+        };
+    in [ layer0 ];
+    }
