diff --git a/lambdabot-irc-plugins.cabal b/lambdabot-irc-plugins.cabal
--- a/lambdabot-irc-plugins.cabal
+++ b/lambdabot-irc-plugins.cabal
@@ -1,5 +1,5 @@
 name:                   lambdabot-irc-plugins
-version:                5.0.3
+version:                5.1
 
 license:                GPL
 license-file:           LICENSE
@@ -26,7 +26,7 @@
 
 build-type:             Simple
 cabal-version:          >= 1.8
-tested-with:            GHC == 7.6.3, GHC == 7.8.3
+tested-with:            GHC == 7.8.4, GHC == 7.10.3
 
 source-repository head
   type:                 git
@@ -39,7 +39,8 @@
 
   exposed-modules:      Lambdabot.Plugin.IRC
 
-  other-modules:        Lambdabot.Plugin.IRC.IRC
+  other-modules:        Lambdabot.Config.IRC
+                        Lambdabot.Plugin.IRC.IRC
                         Lambdabot.Plugin.IRC.Localtime
                         Lambdabot.Plugin.IRC.Log
                         Lambdabot.Plugin.IRC.Topic
@@ -49,7 +50,7 @@
                         containers              >= 0.4,
                         directory               >= 1.1,
                         filepath                >= 1.3,
-                        lambdabot-core          >= 5.0.3 && < 5.1,
+                        lambdabot-core          >= 5.1 && < 5.2,
                         lifted-base             >= 0.2,
                         mtl                     >= 2,
                         network                 >= 2.3.0.13,
diff --git a/src/Lambdabot/Config/IRC.hs b/src/Lambdabot/Config/IRC.hs
new file mode 100644
--- /dev/null
+++ b/src/Lambdabot/Config/IRC.hs
@@ -0,0 +1,11 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -fno-warn-overlapping-patterns #-}
+module Lambdabot.Config.IRC
+    ( reconnectDelay
+    ) where
+
+import Lambdabot.Config
+
+config "reconnectDelay" [t| Int |] [| 10000000 |]
diff --git a/src/Lambdabot/Plugin/IRC/IRC.hs b/src/Lambdabot/Plugin/IRC/IRC.hs
--- a/src/Lambdabot/Plugin/IRC/IRC.hs
+++ b/src/Lambdabot/Plugin/IRC/IRC.hs
@@ -7,15 +7,18 @@
 import Lambdabot.Monad
 import Lambdabot.Plugin
 import Lambdabot.Util
+import Lambdabot.Config.IRC
 
 import Control.Concurrent.Lifted
 import qualified Control.Concurrent.SSem as SSem
 import Control.Exception.Lifted as E (SomeException(..), throwIO, catch)
 import Control.Monad
 import Control.Monad.Trans
+import Control.Monad.State
 import qualified Data.ByteString.Char8 as P
 import Data.List
 import Data.List.Split
+import qualified Data.Map as M
 import Network( connectTo, PortID(..) )
 import System.IO
 import System.Timeout.Lifted
@@ -41,6 +44,17 @@
                         lift (online tag hostn pn nickn (intercalate " " uix))
                     _ -> say "Not enough parameters!"
             }
+        , (command "irc-persist-connect")
+            { privileged = True
+            , help = say "irc-persist-connect tag host portnum nickname userinfo.  connect to an irc server and reconnect on network failures"
+            , process = \rest ->
+                case splitOn " " rest of
+                    tag:hostn:portn:nickn:uix -> do
+                        pn <- (PortNumber . fromInteger) `fmap` readM portn
+                        lift (online tag hostn pn nickn (intercalate " " uix))
+                        lift $ lift $ modify $ \state' -> state' { ircPersists = M.insert tag True $ ircPersists state' }
+                    _ -> say "Not enough parameters!"
+            }
         , (command "irc-password")
             { privileged = True
             , help = say "irc-password pwd.  set password for next irc-connect command"
@@ -125,42 +139,77 @@
 
 online :: String -> String -> PortID -> String -> String -> IRC ()
 online tag hostn portnum nickn ui = do
-    sock    <- io $ connectTo hostn portnum
-    io $ hSetBuffering sock NoBuffering
-    -- Implements flood control: RFC 2813, section 5.8
-    sem1    <- io $ SSem.new 0
-    sem2    <- io $ SSem.new 4 -- one extra token stays in the MVar
-    sendmv  <- io newEmptyMVar
-    pongref <- io $ newIORef False
-    io . void . fork . forever $ do
-        SSem.wait sem1
-        threadDelay 2000000
-        SSem.signal sem2
-    io . void . fork . forever $ do
-        SSem.wait sem2
-        putMVar sendmv ()
-        SSem.signal sem1
-    E.catch 
-        (addServer tag (io . sendMsg sock sendmv))
-        (\err@SomeException{} -> io (hClose sock) >> E.throwIO err)
     pwd <- password `fmap` readMS
     modifyMS $ \ms -> ms{ password = Nothing }
-    lb $ ircSignOn hostn (Nick tag nickn) pwd ui
-    lb . void . fork $ E.catch
-        (readerLoop tag nickn pongref sock)
-        (\e@SomeException{} -> do
-            errorM (show e)
-            remServer tag)
-    lb . void . fork $ E.catch
-        (pingPongDelay >> pingPongLoop tag hostn pongref sock)
-        (\e@SomeException{} -> do
-            errorM (show e)
-            remServer tag)
 
-pingPongDelay :: LB ()
+    let online' = do
+        sock    <- io $ connectTo hostn portnum
+        io $ hSetBuffering sock NoBuffering
+        -- Implements flood control: RFC 2813, section 5.8
+        sem1    <- io $ SSem.new 0
+        sem2    <- io $ SSem.new 4 -- one extra token stays in the MVar
+        sendmv  <- io newEmptyMVar
+        pongref <- io $ newIORef False
+        io . void . fork . forever $ do
+            SSem.wait sem1
+            threadDelay 2000000
+            SSem.signal sem2
+        io . void . fork . forever $ do
+            SSem.wait sem2
+            putMVar sendmv ()
+            SSem.signal sem1
+        fin <- io $ SSem.new 0
+        E.catch
+            (registerServer tag (io . sendMsg sock sendmv fin))
+            (\err@SomeException{} -> io (hClose sock) >> E.throwIO err)
+        lb $ ircSignOn hostn (Nick tag nickn) pwd ui
+        ready <- io $ SSem.new 0
+        lb $ void $ forkFinally
+            (E.catch
+                (readerLoop tag nickn pongref sock ready)
+                (\e@SomeException{} -> errorM (show e)))
+            (const $ io $ SSem.signal fin)
+        void $ forkFinally
+            (E.catch
+                (pingPongDelay >> pingPongLoop tag hostn pongref sock)
+                (\e@SomeException{} -> errorM (show e)))
+            (const $ io $ SSem.signal fin)
+        void $ fork $ do
+            io $ SSem.wait fin
+            unregisterServer tag
+            io $ hClose sock
+            io $ SSem.signal ready
+            delay <- getConfig reconnectDelay
+            let retry = do
+                continue <- lift $ gets $ \st -> (M.member tag $ ircPersists st) && not (M.member tag $ ircServerMap st)
+                if continue
+                    then do
+                        E.catch online'
+                            (\e@SomeException{} -> do
+                                errorM (show e)
+                                io $ threadDelay delay
+                                retry
+                            )
+                    else do
+                        chans <- lift $ gets ircChannels
+                        forM_ (M.keys chans) $ \chan ->
+                            when (nTag (getCN chan) == tag) $
+                            lift $ modify $ \state' -> state' { ircChannels = M.delete chan $ ircChannels state' }
+
+            retry
+        watch <- io $ fork $ do
+            threadDelay 10000000
+            errorM "Welcome timeout!"
+            SSem.signal fin
+        io $ SSem.wait ready
+        killThread watch
+
+    online'
+
+pingPongDelay :: IRC ()
 pingPongDelay = io $ threadDelay 120000000
 
-pingPongLoop :: String -> String -> IORef Bool -> Handle -> LB ()
+pingPongLoop :: String -> String -> IORef Bool -> Handle -> IRC ()
 pingPongLoop tag hostn pongref sock = do
     io $ writeIORef pongref False
     io $ P.hPut sock $ P.pack $ "PING " ++ hostn ++ "\r\n"
@@ -168,10 +217,10 @@
     pong <- io $ readIORef pongref
     if pong
         then pingPongLoop tag hostn pongref sock
-        else errorM "Ping timeout." >> remServer tag
+        else errorM "Ping timeout."
 
-readerLoop :: String -> String -> IORef Bool -> Handle -> LB ()
-readerLoop tag nickn pongref sock = forever $ do
+readerLoop :: String -> String -> IORef Bool -> Handle -> SSem.SSem -> LB ()
+readerLoop tag nickn pongref sock ready = forever $ do
     line <- io $ hGetLine sock
     let line' = filter (`notElem` "\r\n") line
     if "PING " `isPrefixOf` line'
@@ -180,11 +229,13 @@
             let msg = decodeMessage tag nickn line'
             if ircMsgCommand msg == "PONG"
                 then io $ writeIORef pongref True
-                else received msg
+                else do
+                    when (ircMsgCommand msg == "001") $ io $ SSem.signal ready
+                    received msg
 
-sendMsg :: Handle -> MVar () -> IrcMessage -> IO ()
-sendMsg sock mv msg =
+sendMsg :: Handle -> MVar () -> SSem.SSem -> IrcMessage -> IO ()
+sendMsg sock mv fin msg =
     E.catch (do takeMVar mv
                 P.hPut sock $ P.pack $ encodeMessage msg "\r\n")
             (\err -> do errorM (show (err :: IOError))
-                        hClose sock)
+                        SSem.signal fin)
diff --git a/src/Lambdabot/Plugin/IRC/Log.hs b/src/Lambdabot/Plugin/IRC/Log.hs
--- a/src/Lambdabot/Plugin/IRC/Log.hs
+++ b/src/Lambdabot/Plugin/IRC/Log.hs
@@ -7,9 +7,9 @@
 --
 module Lambdabot.Plugin.IRC.Log (logPlugin) where
 
-import Lambdabot.Bot
 import Lambdabot.Compat.FreenodeNick
 import Lambdabot.IRC
+import Lambdabot.Monad
 import qualified Lambdabot.Message as Msg
 import Lambdabot.Nick
 import Lambdabot.Plugin
@@ -64,12 +64,11 @@
     , moduleExit      = cleanLogState
     , moduleInit      = do
         let doLog f m hdl = logString hdl . show . f m
-            wrapCB f = bindModule1 $ \msg -> do
+            connect signal cb = registerCallback signal $ \msg -> do
                 now <- io getCurrentTime
                 -- map over the channels this message was directed to, adding to each
                 -- of their log files.
-                mapM_ (withValidLog (doLog f msg) now) (Msg.channels msg)
-            connect signal cb = ircSignalConnect signal =<< wrapCB cb
+                mapM_ (withValidLog (doLog cb msg) now) (Msg.channels msg)
 
         connect "PRIVMSG" msgCB
         connect "JOIN"    joinCB
