diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,10 @@
+# Changelog for gargoyle
+
+## 0.1.1
+
+* Bugfix: Error cases on opening file were non-exhaustive
+* Loosen version bounds
+
+## 0.1
+
+* Initial release
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+# gargoyle
+
+Gargoyle is a framework for managing daemons from Haskell. Currently, the only requirement is that the daemon be able to communicate over a Unix domain socket. See [gargoyle-postgresql](https://hackage.haskell.org/package/gargoyle-postgresql) for an example that uses gargoyle to manage postgresql.
+
+To use Gargoyle the client must:
+* Define a value of the 'Gargoyle' type which specifies how to administer the daemon.
+* Create an executable whose `main` is `gargoyleMain`. The name of this executable should match the executable name specified in the `_gargoyle_exec` field of the `Gargoyle`.
+* The client will run their code with `withGargoyle` to gain access to the daemon.
+
+# Importing into Haskell package set
+
+```nix
+haskellPackages.override {
+  overrides = self: super:
+    let gargoylePkgs = import ./path/to/gargoyle-repo { haskellPackages = self; };
+    in gargoylePkgs // {
+      # .. your overrides
+    };
+}
+```
+
+By default `gargoyle-postgresql-nix` will use the `postgresql` of the `pkgs` used by your `haskellPackages`. To override this, pass `postgresql` by changing the above line to look more like
+
+```nix
+gargoylePkgs = import ./path/to/gargoyle-repo { haskellPackages = self; postgresql = myCustomVersion; }
+```
+
+# Hacking
+
+Do something like this:
+
+```shell
+nix-shell -A gargoyle-postgresql.env --run 'cd gargoyle-postgresql && cabal new-repl'
+```
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/gargoyle.cabal b/gargoyle.cabal
--- a/gargoyle.cabal
+++ b/gargoyle.cabal
@@ -1,14 +1,14 @@
-name: gargoyle
-version: 0.1
-license: BSD3
-license-file: LICENSE
-author: Obsidian Systems LLC
-maintainer: maintainer@obsidian.systems
-copyright: Copyright (C) Obsidian Systems LLC 2017
-category: System
-build-type: Simple
-cabal-version: >=1.10
-synopsis: Automatically spin up and spin down local daemons
+name:               gargoyle
+version:            0.1.1.0
+license:            BSD3
+license-file:       LICENSE
+author:             Obsidian Systems LLC
+maintainer:         maintainer@obsidian.systems
+copyright:          Copyright (C) Obsidian Systems LLC 2017
+category:           System
+build-type:         Simple
+cabal-version:      >=1.10
+synopsis:           Automatically spin up and spin down local daemons
 description:
   Gargoyle is a framework for managing daemons from Haskell. In the current release the only requirement is that the daemon be able to communicate over a Unix domain socket. See <https://hackage.haskell.org/package/gargoyle-postgresql gargoyle-postgresql> for a fully worked out example.
   .
@@ -18,15 +18,24 @@
   2. Create an executable whose `main` is 'gargoyleMain'. The name of this executable should match the executable name specified in the '_gargoyle_exec' field of the 'Gargoyle'.
   3. The client will run their code with 'withGargoyle' to gain access to the daemon.
 
+extra-source-files:
+  ChangeLog.md
+  README.md
+
+tested-with:        GHC ==8.6.5 || ==8.8.4 || ==8.10.2
+
 library
-  exposed-modules: Gargoyle
-  ghc-options: -Wall
-  ghc-prof-options: -fprof-auto
-  build-depends: base >= 4.9 && < 4.11
-               , directory == 1.3.*
-               , filepath == 1.4.*
-               , filelock == 0.1.*
-               , network == 2.6.*
-               , process == 1.4.*
-  hs-source-dirs: src
+  exposed-modules:  Gargoyle
+  ghc-options:      -Wall
+  ghc-prof-options: -fprof-auto-exported
+  build-depends:
+      base       >=4.12.0 && <4.15
+    , directory  >=1.3.3  && <1.4
+    , filelock   >=0.1.1  && <0.2
+    , filepath   >=1.4.2  && <1.5
+    , network    >=2.6.0  && <3.2
+    , process    >=1.5.0  && <1.7
+    , unix       >=2.7.2  && <2.8
+
+  hs-source-dirs:   src
   default-language: Haskell2010
diff --git a/src/Gargoyle.hs b/src/Gargoyle.hs
--- a/src/Gargoyle.hs
+++ b/src/Gargoyle.hs
@@ -20,6 +20,7 @@
 import System.IO.Error
 import System.Environment
 import System.FileLock
+import System.Posix.Process
 import System.Process
 
 import Debug.Trace
@@ -92,7 +93,10 @@
               let monProc = (proc (_gargoyle_exec g) [daemonDir])
                     { std_in = CreatePipe
                     , std_out = CreatePipe
-                    , std_err = Inherit }
+                    , std_err = Inherit
+                    , close_fds = True
+                    , new_session = True
+                    }
               (Just monIn, Just monOut, Nothing, monHnd) <- createProcess monProc
               void $ forkOS $ void $ waitForProcess monHnd
               hClose monIn
@@ -102,7 +106,7 @@
                   threadDelay 500000 -- These are expensive ops so don't try too hard
                   acquire -- Try again
                 "ready" -> acquire
-                _ -> fail "Unexpected gargoyle message from monitor process"
+                _ -> fail $ "Unexpected gargoyle message from monitor process: " <> r
             | otherwise -> throwIO e
   bracket_ acquire (shutdown s ShutdownBoth >> close s) $
     b =<< _gargoyle_getInfo g (gWorkDir daemonDir)
@@ -115,7 +119,7 @@
 gargoyleMain :: Gargoyle pid a
              -- ^ Description of how to initialize, spin up, and spin down a daemon.
              -> IO () -- ^ Returns only when all clients have disconnected.
-gargoyleMain g = do
+gargoyleMain g = void $ forkProcess $ do
   checkThreadedRuntime
   [daemonDir] <- getArgs >>= \case
     x@[_] -> return x
@@ -126,6 +130,7 @@
     | isAlreadyInUseError e -> return ()
     | isDoesNotExistError e -> throwIO e -- this means it's not a file but it exists
     | isPermissionError e -> throwIO e -- the daemon directory is in a bad state
+    | otherwise -> throwIO e
   -- The daemon tries to hold on to the lock file for its lifetime, signaling that it is
   -- accepting connections.
   lock <- tryLockFile lockPath Exclusive >>= \case
@@ -166,6 +171,7 @@
     shutdownVar <- newEmptyMVar
     void $ forkOS $ forever $ do
       (s, _) <- accept controlSocket
+      acceptThread <- myThreadId
       --TODO: What happens if we decide we're shutting down here?
       modifyMVar_ numClientsVar $ \n -> do
         return $ succ n
@@ -182,6 +188,8 @@
           case pred n of
             0 -> do
               shutdown controlSocket ShutdownBoth
+              -- We have to explicitly kill the accept thread, because otherwise (sometimes?) 'accept' will begin returning EAGAIN, and ghc will continuously retry it.  This busywait consumes 100% of CPU, prevents the monitor from actually exiting, and leaves the control socket and lock file in a state where another monitor can't be started.
+              killThread acceptThread
               putMVar shutdownVar ()
             n' -> putMVar numClientsVar n'
     bracket (_gargoyle_start g (gWorkDir daemonDir)) (_gargoyle_stop g) $ \_ -> do
