diff --git a/Data/Conduit/Async.hs b/Data/Conduit/Async.hs
--- a/Data/Conduit/Async.hs
+++ b/Data/Conduit/Async.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE RecordWildCards #-}
 
 -- | * Introduction
 --
@@ -9,20 +10,30 @@
 --   the consumer is concurrently consuming.
 module Data.Conduit.Async ( buffer
                           , ($$&)
+                          , bufferToFile
                           , gatherFrom
                           , drainTo
                           ) where
 
-import Control.Applicative
-import Control.Concurrent.Async.Lifted
-import Control.Concurrent.STM
-import Control.Exception.Lifted
-import Control.Monad.IO.Class
-import Control.Monad.Loops
-import Control.Monad.Trans.Class
-import Control.Monad.Trans.Control
-import Data.Conduit
-import Data.Conduit.List as CL
+import           Control.Applicative
+import           Control.Concurrent.Async.Lifted
+import           Control.Concurrent.STM
+import           Control.Concurrent.STM.TBChan
+import           Control.Exception.Lifted
+import           Control.Monad hiding (forM_)
+import           Control.Monad.IO.Class
+import           Control.Monad.Loops
+import           Control.Monad.Trans.Class
+import           Control.Monad.Trans.Control
+import           Control.Monad.Trans.Resource
+import           Data.Conduit
+import qualified Data.Conduit.Binary as CB
+import qualified Data.Conduit.Cereal as C
+import qualified Data.Conduit.List as CL
+import           Data.Foldable (forM_)
+import           Data.Serialize as Cereal
+import           System.Directory (removeFile)
+import           System.IO
 
 -- | Concurrently join the producer and consumer, using a bounded queue of the
 --   given size.  The producer will block when the queue is full, if it is
@@ -38,9 +49,9 @@
     chan <- liftIO $ newTBQueueIO size
     control $ \runInIO ->
         withAsync (runInIO $ sender chan) $ \input' ->
-            withAsync (runInIO $ recv chan $$ output) $ \output' -> do
-                link2 input' output'
-                wait output'
+        withAsync (runInIO $ recv chan $$ output) $ \output' -> do
+            link2 input' output'
+            wait output'
   where
     send chan = liftIO . atomically . writeTBQueue chan
 
@@ -60,6 +71,96 @@
 ($$&) :: (MonadIO m, MonadBaseControl IO m)
       => Producer m a -> Consumer a m b -> m b
 ($$&) = buffer 64
+
+data BufferContext m a = BufferContext
+    { chan      :: TBChan a
+    , restore   :: TChan (Source m a)
+    , slotsFree :: TVar (Maybe Int)
+    , done      :: TVar Bool
+    }
+
+-- | Like 'buffer', except that when the bounded queue is overflowed, the
+--   excess is cached in a local file so that consumption from upstream may
+--   continue.  When the queue becomes exhausted by yielding, it is filled
+--   from the cache until all elements have been yielded.
+--
+--   Note that the maximum amount of memory consumed is equal to (2 *
+--   memorySize + 1), so take this into account when picking a chunking size.
+bufferToFile :: (MonadBaseControl IO m, MonadIO m, MonadResource m, Serialize a)
+             => Int              -- ^ Size of the bounded queue in memory
+             -> Maybe Int        -- ^ Max elements to keep on disk at one time
+             -> FilePath         -- ^ Directory to write temp files to
+             -> Producer m a
+             -> Consumer a m b
+             -> m b
+bufferToFile memorySize fileMax tempDir input output = do
+    context <- liftIO $ BufferContext
+        <$> newTBChanIO memorySize
+        <*> newTChanIO
+        <*> newTVarIO fileMax
+        <*> newTVarIO False
+    control $ \runInIO ->
+        withAsync (runInIO $ sender context) $ \input' ->
+        withAsync (runInIO $ recv context $$ output) $ \output' -> do
+            link2 input' output'
+            wait output'
+  where
+    sender BufferContext {..} = do
+        input $$ awaitForever $ \x -> join $ liftIO $ atomically $ do
+            written <- tryWriteTBChan chan x
+            if written
+                then return $ return ()
+                else do
+                    action <- persistChan
+                    writeTBChan chan x
+                    return action
+        liftIO $ atomically $ writeTVar done True
+      where
+        persistChan = do
+            -- Empty the pending chan and return an action that writes the
+            -- overflow to a disk file.
+            xs <- exhaust chan
+            mslots <- readTVar slotsFree
+            let len = length xs
+            forM_ mslots $ \slots -> check (len < slots)
+
+            filePath <- newEmptyTMVar
+            writeTChan restore $ do
+                (path, key) <- liftIO $ atomically $ takeTMVar filePath
+                CB.sourceFile path $= do
+                    C.conduitGet Cereal.get
+                    liftIO $ atomically $
+                        modifyTVar slotsFree (fmap (+ len))
+                    release key
+
+            case xs of
+                [] -> return $ return ()
+                _  -> do
+                    modifyTVar slotsFree (fmap (+ (-len)))
+                    return $ do
+                        (key, (path, h)) <- allocate
+                            (openTempFile tempDir "conduit.bin")
+                            (\(path, h) -> hClose h >> removeFile path)
+                        liftIO $ do
+                            CL.sourceList xs $= C.conduitPut put
+                                $$ CB.sinkHandle h
+                            hClose h
+                            atomically $ putTMVar filePath (path, key)
+
+    recv BufferContext {..} = loop where
+        loop = do
+            (src, exit) <- liftIO $ atomically $ do
+                maction <- tryReadTChan restore
+                case maction of
+                    Just action -> return (action, False)
+                    Nothing -> do
+                        xs <- exhaust chan
+                        isDone <- readTVar done
+                        return (CL.sourceList xs, isDone)
+            src
+            unless exit loop
+
+    exhaust chan = whileM (not <$> isEmptyTBChan chan) (readTBChan chan)
 
 -- | Gather output values asynchronously from an action in the base monad and
 --   then yield them downstream.  This provides a means of working around the
diff --git a/stm-conduit.cabal b/stm-conduit.cabal
--- a/stm-conduit.cabal
+++ b/stm-conduit.cabal
@@ -1,5 +1,5 @@
 Name:                stm-conduit
-Version:             2.2
+Version:             2.2.1
 Synopsis:            Introduces conduits to channels, and promotes using
                      conduits concurrently.
 Description:         Provides two simple conduit wrappers around STM
@@ -22,17 +22,20 @@
         Data.Conduit.TQueue
 
     build-depends:
-        base          == 4.*
-      , transformers  >= 0.2 && <= 0.4
-      , stm           == 2.4.*
-      , stm-chans     >= 2.0 && < 3.1
-      , conduit       == 1.0.*
-      , resourcet     >= 0.3 && < 0.5
-      , async         >= 2.0.1
-      , monad-control >= 0.3.2
-      , monad-loops   >= 0.4.2
-      , lifted-base   >= 0.2.1
-      , lifted-async  >= 0.1
+        base           == 4.*
+      , transformers   >= 0.2 && <= 0.4
+      , stm            == 2.4.*
+      , stm-chans      >= 2.0 && < 3.1
+      , cereal         >= 0.4.0.1
+      , cereal-conduit >= 0.7.2
+      , conduit        == 1.0.*
+      , directory      >= 1.2
+      , resourcet      >= 0.3 && < 0.5
+      , async          >= 2.0.1
+      , monad-control  >= 0.3.2
+      , monad-loops    >= 0.4.2
+      , lifted-base    >= 0.2.1
+      , lifted-async   >= 0.1
 
     ghc-options: -Wall -fwarn-tabs -fwarn-unused-imports
 
@@ -55,6 +58,8 @@
       , conduit
       , transformers
       , stm-chans
+      , resourcet
+      , directory
 
 source-repository head
     type:     git
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -12,6 +12,7 @@
 import qualified Control.Monad as Monad
 import Control.Monad.IO.Class (liftIO)
 import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Resource (runResourceT)
 import Control.Concurrent
 import Control.Concurrent.STM
 import Control.Concurrent.STM.TMQueue
@@ -20,6 +21,7 @@
 import Data.Conduit.Async
 import Data.Conduit.TMChan
 import Data.Conduit.TQueue
+import System.Directory
 
 main = defaultMain tests
 
@@ -31,6 +33,7 @@
             ],
         testGroup "Async functions" [
                   testCase "buffer" test_buffer
+                , testCase "bufferToFile" test_bufferToFile
                 , testCase "gatherFrom" test_gatherFrom
                 , testCase "drainTo" test_drainTo
             ],
@@ -82,6 +85,11 @@
 test_buffer = do
     sum' <- buffer 128 (CL.sourceList [1..100]) (CL.fold (+) 0)
     assertEqual "sum computed using buffer" sum' 5050
+
+test_bufferToFile = do
+    tempDir <- getTemporaryDirectory
+    sum' <- runResourceT $ bufferToFile 16 (Just 5) tempDir (CL.sourceList [1 :: Int .. 100]) (CL.fold (+) 0)
+    assertEqual "sum computed using bufferToFile" sum' 5050
 
 test_gatherFrom = do
     sum' <- gatherFrom 128 gen $$ CL.fold (+) 0
