diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## Changes in 0.27.0
+  - Local defaults are now resolved relative to the file they are
+    mentioned in, not the CWD that hpack is invoked from.
+
 ## Changes in 0.26.0
   - Major refactoring of the exposed API (much cleaner now, but lot's of
     breaking changes!)
diff --git a/hpack.cabal b/hpack.cabal
--- a/hpack.cabal
+++ b/hpack.cabal
@@ -1,11 +1,11 @@
--- This file has been generated from package.yaml by hpack version 0.25.0.
+-- This file has been generated from package.yaml by hpack version 0.26.0.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 6affba6bd6791822c191936fdc6314baff1c094aaaebbdd3bf1020f907131a8e
+-- hash: 3172713e09611ee19cc9d92dafaabdc4ca26daada30517f9458fa486d4ae02ba
 
 name:           hpack
-version:        0.26.0
+version:        0.27.0
 synopsis:       An alternative format for Haskell packages
 description:    See README at <https://github.com/sol/hpack#readme>
 category:       Development
diff --git a/src/Hpack/Config.hs b/src/Hpack/Config.hs
--- a/src/Hpack/Config.hs
+++ b/src/Hpack/Config.hs
@@ -686,34 +686,37 @@
 
 toPackage :: FilePath -> FilePath -> ConfigWithDefaults -> Warnings (Errors IO) Package
 toPackage userDataDir dir =
-      expandDefaultsInConfig userDataDir
+      expandDefaultsInConfig userDataDir dir
   >=> traverseConfig (expandForeignSources dir)
   >=> toPackage_ dir
 
 expandDefaultsInConfig
   :: FilePath
+  -> FilePath
   -> ConfigWithDefaults
   -> Warnings (Errors IO) (Config ParseCSources ParseJsSources)
-expandDefaultsInConfig userDataDir = bitraverse (expandGlobalDefaults userDataDir) (expandSectionDefaults userDataDir)
+expandDefaultsInConfig userDataDir dir = bitraverse (expandGlobalDefaults userDataDir dir) (expandSectionDefaults userDataDir dir)
 
 expandGlobalDefaults
   :: FilePath
+  -> FilePath
   -> CommonOptionsWithDefaults Empty
   -> Warnings (Errors IO) (CommonOptions ParseCSources ParseJsSources Empty)
-expandGlobalDefaults userDataDir = do
-  fmap (`Product` Empty) >>> expandDefaults userDataDir >=> \ (Product c Empty) -> return c
+expandGlobalDefaults userDataDir dir = do
+  fmap (`Product` Empty) >>> expandDefaults userDataDir dir >=> \ (Product c Empty) -> return c
 
 expandSectionDefaults
   :: FilePath
+  -> FilePath
   -> PackageConfigWithDefaults ParseCSources ParseJsSources
   -> Warnings (Errors IO) (PackageConfig ParseCSources ParseJsSources)
-expandSectionDefaults userDataDir p@PackageConfig{..} = do
-  library <- traverse (expandDefaults userDataDir) packageConfigLibrary
-  internalLibraries <- traverse (traverse (expandDefaults userDataDir)) packageConfigInternalLibraries
-  executable <- traverse (expandDefaults userDataDir) packageConfigExecutable
-  executables <- traverse (traverse (expandDefaults userDataDir)) packageConfigExecutables
-  tests <- traverse (traverse (expandDefaults userDataDir)) packageConfigTests
-  benchmarks <- traverse (traverse (expandDefaults userDataDir)) packageConfigBenchmarks
+expandSectionDefaults userDataDir dir p@PackageConfig{..} = do
+  library <- traverse (expandDefaults userDataDir dir) packageConfigLibrary
+  internalLibraries <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigInternalLibraries
+  executable <- traverse (expandDefaults userDataDir dir) packageConfigExecutable
+  executables <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigExecutables
+  tests <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigTests
+  benchmarks <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigBenchmarks
   return p{
       packageConfigLibrary = library
     , packageConfigInternalLibraries = internalLibraries
@@ -726,26 +729,30 @@
 expandDefaults
   :: (FromValue a, Monoid a)
   => FilePath
+  -> FilePath
   -> WithCommonOptionsWithDefaults a
   -> Warnings (Errors IO) (WithCommonOptions ParseCSources ParseJsSources a)
 expandDefaults userDataDir = expand []
   where
     expand :: (FromValue a, Monoid a) =>
          [FilePath]
+      -> FilePath
       -> WithCommonOptionsWithDefaults a
       -> Warnings (Errors IO) (WithCommonOptions ParseCSources ParseJsSources a)
-    expand seen (Product DefaultsConfig{..} c) = do
-      d <- mconcat <$> mapM (get seen) (fromMaybeList defaultsConfigDefaults)
+    expand seen dir (Product DefaultsConfig{..} c) = do
+      d <- mconcat <$> mapM (get seen dir) (fromMaybeList defaultsConfigDefaults)
       return (d <> c)
 
     get :: forall a. (FromValue a, Monoid a) =>
          [FilePath]
+      -> FilePath
       -> Defaults
       -> Warnings (Errors IO) (WithCommonOptions ParseCSources ParseJsSources a)
-    get seen defaults = do
-      file <- lift $ ExceptT (ensure userDataDir defaults)
+    get seen dir defaults = do
+      file <- lift $ ExceptT (ensure userDataDir dir defaults)
       seen_ <- lift (checkCycle seen file)
-      decodeYaml file >>= expand seen_
+      let dir_ = takeDirectory file
+      decodeYaml file >>= expand seen_ dir_
 
     checkCycle :: [FilePath] -> FilePath -> Errors IO [FilePath]
     checkCycle seen file = do
diff --git a/src/Hpack/Defaults.hs b/src/Hpack/Defaults.hs
--- a/src/Hpack/Defaults.hs
+++ b/src/Hpack/Defaults.hs
@@ -51,17 +51,17 @@
 formatStatus :: Status -> String
 formatStatus (Status code message) = show code ++ " " ++ B.unpack message
 
-ensure :: FilePath -> Defaults -> IO (Either String FilePath)
-ensure dir = \ case
+ensure :: FilePath -> FilePath -> Defaults -> IO (Either String FilePath)
+ensure userDataDir dir = \ case
   DefaultsGithub defaults -> do
     let
       url = defaultsUrl defaults
-      file = defaultsCachePath dir defaults
+      file = defaultsCachePath userDataDir defaults
     ensureFile file url >>= \ case
       Found -> return (Right file)
       NotFound -> return (Left $ notFound url)
       Failed err -> return (Left err)
-  DefaultsLocal (Local file) ->
+  DefaultsLocal (Local ((dir </>) -> file)) -> do
     doesFileExist file >>= \ case
       True -> return (Right file)
       False -> return (Left $ notFound file)
diff --git a/test/EndToEndSpec.hs b/test/EndToEndSpec.hs
--- a/test/EndToEndSpec.hs
+++ b/test/EndToEndSpec.hs
@@ -11,7 +11,7 @@
 import           Helper
 import           Test.HUnit
 
-import           System.Directory (canonicalizePath)
+import           System.Directory (canonicalizePath, createDirectory)
 import           Data.Maybe
 import           Data.List
 import           Data.String.Interpolate
@@ -233,14 +233,20 @@
           ]
 
       it "accepts defaults from local files" $ do
-        writeFile "defaults.yaml" [i|
+        writeFile "defaults/foo.yaml" [i|
+        defaults:
+          local: bar.yaml
+        |]
+
+        writeFile "defaults/bar.yaml" [i|
         default-extensions:
           - RecordWildCards
           - DeriveFunctor
         |]
+
         [i|
         defaults:
-          local: defaults.yaml
+          local: defaults/foo.yaml
         library: {}
         |] `shouldRenderTo` library [i|
         other-modules:
@@ -1176,12 +1182,12 @@
             default-language: Haskell2010
           |]
 
-run :: HasCallStack => FilePath -> String -> IO ([String], String)
-run c old = run_ c old >>= either assertFailure return
+run :: HasCallStack => FilePath -> FilePath -> String -> IO ([String], String)
+run userDataDir c old = run_ userDataDir c old >>= either assertFailure return
 
-run_ :: FilePath -> String -> IO (Either String ([String], String))
-run_ c old = do
-  mPackage <- readPackageConfig defaultDecodeOptions {decodeOptionsTarget = c, decodeOptionsUserDataDir = Just ""}
+run_ :: FilePath -> FilePath -> String -> IO (Either String ([String], String))
+run_ userDataDir c old = do
+  mPackage <- readPackageConfig defaultDecodeOptions {decodeOptionsTarget = c, decodeOptionsUserDataDir = Just userDataDir}
   return $ case mPackage of
     Right (DecodeResult pkg _ warnings) ->
       let
@@ -1202,8 +1208,11 @@
 shouldRenderTo :: HasCallStack => String -> Package -> Expectation
 shouldRenderTo input p = do
   writeFile packageConfig ("name: foo\n" ++ unindent input)
-  (warnings, output) <- run packageConfig expected
-  RenderResult warnings (dropEmptyLines output) `shouldBe` RenderResult [] expected
+  let currentDirectory = ".working-directory"
+  createDirectory currentDirectory
+  withCurrentDirectory currentDirectory $ do
+    (warnings, output) <- run ".." (".." </> packageConfig) expected
+    RenderResult warnings (dropEmptyLines output) `shouldBe` RenderResult [] expected
   where
     expected = dropEmptyLines (renderPackage p)
     dropEmptyLines = unlines . filter (not . null) . lines
@@ -1211,13 +1220,13 @@
 shouldWarn :: HasCallStack => String -> [String] -> Expectation
 shouldWarn input expected = do
   writeFile packageConfig input
-  (warnings, _) <- run packageConfig ""
+  (warnings, _) <- run "" packageConfig ""
   sort warnings `shouldBe` sort expected
 
 shouldFailWith :: HasCallStack => String -> String -> Expectation
 shouldFailWith input expected = do
   writeFile packageConfig input
-  run_ packageConfig "" `shouldReturn` Left expected
+  run_ "" packageConfig "" `shouldReturn` Left expected
 
 customSetup :: String -> Package
 customSetup a = (package content) {packageCabalVersion = ">= 1.24", packageBuildType = "Custom"}
diff --git a/test/Hpack/DefaultsSpec.hs b/test/Hpack/DefaultsSpec.hs
--- a/test/Hpack/DefaultsSpec.hs
+++ b/test/Hpack/DefaultsSpec.hs
@@ -11,7 +11,9 @@
 spec = do
   describe "ensure" $ do
     it "fails when local file does not exist" $ do
-      ensure undefined (DefaultsLocal $ Local "foo") `shouldReturn` Left "Invalid value for \"defaults\"! File foo does not exist!"
+      cwd <- getCurrentDirectory
+      let expected = Left $ "Invalid value for \"defaults\"! File " ++ (cwd </> "foo") ++ " does not exist!"
+      ensure undefined cwd (DefaultsLocal $ Local "foo") `shouldReturn` expected
 
   describe "ensureFile" $ do
     let
diff --git a/test/Hpack/OptionsSpec.hs b/test/Hpack/OptionsSpec.hs
--- a/test/Hpack/OptionsSpec.hs
+++ b/test/Hpack/OptionsSpec.hs
@@ -69,7 +69,7 @@
 
     context "when target directory does not exist" $ do
       it "appends default file" $ do
-        expandTarget defaultTarget (Just "foo/") `shouldReturn` "foo" </> defaultTarget
+        expandTarget defaultTarget (Just "foo/") `shouldReturn` ("foo/" ++ defaultTarget)
 
     context "when target is the empty string" $ do
       it "return default file" $ do
