1. Install mod_fcgid. On Debian-derived systems, "sudo apt-get install libapache2-mod-fcgid" To install from the source and to learn more, http://httpd.apache.org/mod_fcgid/ 2. Choose a directory for the Yesod application that will run as fcgi. I my example, I will use /usr/local/YBother. The name of the app binary will be "YBother" too. 3. In your Apache configuration, create a section for the YBother app running under FastCGI: # Use "/" if the whole VirtualHost is the app Options ExecCGI Order allow,deny allow from all SetHandler fcgid-script FcgidWrapper "/usr/local/YBother/YBother Production" virtual It is important to specify the flag 'virtual' in the FcgidWrapper directive. 4. Install the package wai-handler-fastcgi via cabal install. Add the 'build-depends' dependency to your .cabal file: , wai-handler-fastcgi >= 1.1 && < 1.2 5. *** WORKAROUND *** WORKAROUND *** WORKAROUND *** Before proceeding, we must make a workaround for a problem that exists as of mod_fcgid 2.3.6 and wai-extra 1.1.0. The nature of the problem is this: when run in 'virtual' mode, mod_fcgid does not set PATH_INFO variable (and neither PATH_TRANSLATED), presumably because the logic of the implementation of 'virtual' makes it think that any path in the URL is the path of the ("virtual") handler binary itself. Still, it does set the variable SCRIPT_NAME to exactly what the application would like to see as the value of PATH_INFO. Now, I could argue that mod_fcgid *should* set PATH_INFO equal to SCRIPT_NAME and PATH_TRANSLATED equal to SCRIPT_FILENAME when the handler is run in "virtual" mode because intuitively this seems like the Right Thing (tm), and I've filed a bug https://issues.apache.org/bugzilla/show_bug.cgi?id=52710 For now, it is technicaly easier to provide a workaround in the WAI CGI module. Specifically, let us make it take the value of SCRIPT_NAME if PATH_INFO was not present in the request. This is the diff: index 2d9fb47..14ea6b2 100755 --- a/wai-extra/Network/Wai/Handler/CGI.hs +++ b/wai-extra/Network/Wai/Handler/CGI.hs @@ -73,7 +73,13 @@ runGeneric -> IO () runGeneric vars inputH outputH xsendfile app = do let rmethod = B.pack $ lookup' "REQUEST_METHOD" vars - pinfo = lookup' "PATH_INFO" vars + pinfo = + case lookup "PATH_INFO" vars of + Just x -> x + Nothing -> + case lookup "SCRIPT_NAME" vars of + Just x -> x + Nothing -> "" qstring = lookup' "QUERY_STRING" vars servername = lookup' "SERVER_NAME" vars serverport = safeRead 80 $ lookup' "SERVER_PORT" vars Build and install wai-extra package with this diff applied. *** END OF UGLY WORKAROUND *** 6. Change the "main.hs" of your Yesod app to look like this: import Prelude (IO) import Network.Wai.Handler.FastCGI (run) import Yesod.Default.Config (fromArgs) import Yesod.Logger (defaultProductionLogger) import Settings (parseExtra) import Application (getApplication) main :: IO () main = do appconfig <- fromArgs parseExtra logger <- defaultProductionLogger application <- getApplication appconfig logger run application 7. Build the application, and place the binary *AND* the directories "config" and "static" with all their content into the directory from which Apache will execute the handler. Let me stress it: the executable must be in the same directory with the "config" and "static" subdirectories. This is because when Apache starts the executable, it sets the current working directory to the executable's `dirname`. 8. Make the "static/tmp" directory writable to the httpd process. On Debian-derived systems, chgrp -R www-data /usr/local/YBother/static/tmp chmod -R g+w /usr/local/YBother/static/tmp 9. Remember to set "approot" to the correct place, e.g. Production: approot: "http://www.example.com/YBother" <<: *defaults 10.Restart Apache to make it notice the change of configuration. 11.Point your browser to the app root http://www.example.com/YBother/ and verify that things work as expected.