diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -135,7 +135,10 @@
 * To verify just the packaging, without deployment, run
   `./integration-test/run.sh --dry-run`.
 * By default, the integration test is run with LTS 12. To specify a different
-series, use `RESOLVER_SERIES=lts-9`.
+  series, use `RESOLVER_SERIES=lts-9`.
+* To avoid creating a temporary directory for every run, specify
+  `--no-clean-dir`. This can speed up repeated test runs, but does not guarantee
+  the same results as a clean test.
 
 ### Releasing
 
diff --git a/serverless-haskell.cabal b/serverless-haskell.cabal
--- a/serverless-haskell.cabal
+++ b/serverless-haskell.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: ab0280c7dd88a27cf3fb85f9b31bded7565ea33ba48e9803f27112e32664db7f
+-- hash: 07d8137bff2fb73c1d9db9c3ca8bfb538255c2e6fa3be233d7e144a02a0eba0e
 
 name:           serverless-haskell
-version:        0.8.3
+version:        0.8.4
 synopsis:       Deploying Haskell code onto AWS Lambda using Serverless
 description:    Utilities to help process the events from AWS Lambda when deployed with the serverless-haskell plugin.
 category:       AWS, Cloud, Network
@@ -56,6 +56,8 @@
     , http-types
     , iproute
     , lens
+    , network
+    , network-simple
     , text
     , time
     , unix
@@ -92,6 +94,8 @@
     , http-types
     , iproute
     , lens
+    , network
+    , network-simple
     , raw-strings-qq
     , serverless-haskell
     , text
diff --git a/src/AWSLambda/Handler.hs b/src/AWSLambda/Handler.hs
--- a/src/AWSLambda/Handler.hs
+++ b/src/AWSLambda/Handler.hs
@@ -5,28 +5,30 @@
 
 Entry point for AWS Lambda handlers deployed with @serverless-haskell@ plugin.
 -}
-{-# LANGUAGE TypeApplications #-}
-
 module AWSLambda.Handler
   ( lambdaMain
   ) where
 
-import Control.Exception (bracket, try)
+import Control.Exception (finally)
+import Control.Monad (void)
 
 import qualified Data.Aeson as Aeson
-import qualified Data.Aeson.Text as Aeson
 
+import Data.ByteString (ByteString)
 import qualified Data.ByteString as ByteString
+import qualified Data.ByteString.Lazy as LBS
 
-import qualified Data.Text.Lazy.IO as Text
+import qualified Data.Text.Encoding as Text
+import qualified Data.Text.IO as Text
 
-import GHC.IO.Handle
-       (BufferMode(..), Handle, hClose, hSetBuffering)
+import GHC.IO.Handle (BufferMode(..), hSetBuffering)
 
+import Network.Simple.TCP (connect)
+import Network.Socket (close, withSocketsDo)
+import Network.Socket.ByteString (send)
+
+import System.Environment (lookupEnv)
 import System.IO (stdout)
-import System.Posix.Files (getFdStatus)
-import System.Posix.IO (fdToHandle)
-import System.Posix.Types (Fd(..))
 
 -- | Process incoming events from @serverless-haskell@ using a provided
 -- function.
@@ -82,28 +84,31 @@
   => (event -> IO res) -- ^ Function to process the event
   -> IO ()
 lambdaMain act =
-  withResultChannel $ \resultChannel -> do
+  withSendResult $ \sendResult -> do
     input <- ByteString.getLine
     case Aeson.eitherDecodeStrict input of
       Left err -> error err
       Right event -> do
         result <- act event
-        Text.hPutStrLn resultChannel $ Aeson.encodeToLazyText result
+        sendResult $ LBS.toStrict $ Aeson.encode result
         pure ()
 
--- | Invoke an action with the handle to write the results to. If called by the
--- JavaScript wrapper, use the channel opened by it, otherwise use standard
+-- | Invoke an action with the method to output the result. If called by the
+-- JavaScript wrapper, use the server started by it, otherwise use standard
 -- output. Also set line buffering on standard output for AWS Lambda so the logs
 -- are output in a timely manner.
-withResultChannel :: (Handle -> IO r) -> IO r
-withResultChannel act = do
-  commStatus <- try @IOError $ getFdStatus communicationFd
-  case commStatus of
-    Right _ -> do
+withSendResult :: ((ByteString -> IO ()) -> IO r) -> IO r
+withSendResult act = do
+  communicationPortString <- lookupEnv communicationPortEnv
+  case communicationPortString of
+    Just communicationPort -> do
       hSetBuffering stdout LineBuffering
-      bracket (fdToHandle communicationFd) hClose act
-    Left _ -> act stdout
+      withSocketsDo $
+        connect "127.0.0.1" communicationPort $ \(socket, _) ->
+          act (void . send socket) `finally` close socket
+    Nothing -> act $ Text.putStrLn . Text.decodeUtf8
 
--- | File descriptor opened by the JavaScript wrapper to listen for the results
-communicationFd :: Fd
-communicationFd = Fd 3
+-- | Environment variable signalling the port the JavaScript wrapper is
+-- listening on
+communicationPortEnv :: String
+communicationPortEnv = "SERVERLESS_HASKELL_COMMUNICATION_PORT"
