diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,5 @@
+__1.4.0.9__ Allow devel.hs to be located in app/ or src/ subdirectories.
+
 __1.4.0.8__ Updated postgres-fay scaffolding for yesod-fay 0.7.0
 
 __1.4.0.7__ Fix a bug in `yesod devel` when cabal config has `tests: True` #864
diff --git a/Devel.hs b/Devel.hs
--- a/Devel.hs
+++ b/Devel.hs
@@ -188,7 +188,7 @@
     unlessM (checkPort $ develPort opts) $ error "devel port unavailable"
     iappPort <- getPort opts 17834 >>= I.newIORef
     when (useReverseProxy opts) $ void $ forkIO $ reverseProxy opts iappPort
-    checkDevelFile
+    develHsPath <- checkDevelFile
     writeLock opts
 
     let (terminator, after) = case terminateWith opts of
@@ -203,7 +203,7 @@
       filesModified <- newEmptyMVar
       void $ forkIO $
         void $ watchTree manager "." (const True) (\_ -> void (tryPutMVar filesModified ()))
-      evalStateT (mainOuterLoop iappPort filesModified) Map.empty
+      evalStateT (mainOuterLoop develHsPath iappPort filesModified) Map.empty
     after
     writeLock opts
     exitSuccess
@@ -211,7 +211,7 @@
     bd = getBuildDir opts
 
     -- outer loop re-reads the cabal file
-    mainOuterLoop iappPort filesModified = do
+    mainOuterLoop develHsPath iappPort filesModified = do
       ghcVer <- liftIO ghcVersion
       cabal  <- liftIO $ D.findPackageDesc "."
       gpd    <- liftIO $ D.readPackageDescription D.normal cabal
@@ -227,20 +227,20 @@
                                                , "yesod-devel/ldargs.txt"
                                                ]
              rebuild <- liftIO $ mkRebuild ghcVer cabal opts ldar
-             mainInnerLoop iappPort hsSourceDirs filesModified cabal rebuild
+             mainInnerLoop develHsPath iappPort hsSourceDirs filesModified cabal rebuild
            else do
              liftIO (threadDelay 5000000)
-             mainOuterLoop iappPort filesModified
+             mainOuterLoop develHsPath iappPort filesModified
 
     -- inner loop rebuilds after files change
-    mainInnerLoop iappPort hsSourceDirs filesModified cabal rebuild = go
+    mainInnerLoop develHsPath iappPort hsSourceDirs filesModified cabal rebuild = go
        where
          go = do
            _ <- recompDeps hsSourceDirs
            list <- liftIO $ getFileList hsSourceDirs [cabal]
            success <- liftIO rebuild
            pkgArgs <- liftIO (ghcPackageArgs opts)
-           let devArgs = pkgArgs ++ ["devel.hs"]
+           let devArgs = pkgArgs ++ [develHsPath]
            let loop list0 = do
                    (haskellFileChanged, list1) <- liftIO $
                        watchForChanges filesModified hsSourceDirs [cabal] list0 (eventTimeout opts)
@@ -282,7 +282,7 @@
                    liftIO $ Ex.throwTo watchTid (userError "process finished")
            loop list
            n <- liftIO $ cabal `isNewerThan` (bd </> "setup-config")
-           if n then mainOuterLoop iappPort filesModified else go
+           if n then mainOuterLoop develHsPath iappPort filesModified else go
 
 runBuildHook :: Maybe String -> IO ()
 runBuildHook (Just s) = do
@@ -381,10 +381,18 @@
 
     isHaskell filename _ = takeExtension filename `elem` [".hs", ".lhs", ".hsc", ".cabal"]
 
-checkDevelFile :: IO ()
-checkDevelFile = do
-  e <- doesFileExist "devel.hs"
-  unless e $ failWith "file devel.hs not found"
+checkDevelFile :: IO FilePath
+checkDevelFile =
+    loop paths
+  where
+    paths = ["app/devel.hs", "devel.hs", "src/devel.hs"]
+
+    loop [] = failWith $ "file devel.hs not found, checked: " ++ show paths
+    loop (x:xs) = do
+        e <- doesFileExist x
+        if e
+            then return x
+            else loop xs
 
 checkCabalFile :: D.GenericPackageDescription -> IO ([FilePath], D.Library)
 checkCabalFile gpd = case D.condLibrary gpd of
diff --git a/yesod-bin.cabal b/yesod-bin.cabal
--- a/yesod-bin.cabal
+++ b/yesod-bin.cabal
@@ -1,5 +1,5 @@
 name:            yesod-bin
-version:         1.4.0.8
+version:         1.4.0.9
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
