diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Revision history for testcontainer-hs
 
+## 0.5.4.0 -- 2026-07-06
+
+* Added support for Windows containers (@nikita-volkov, https://github.com/testcontainers/testcontainers-hs/pull/72)
+
+* Default image versions updated for Redis, Mongo, RabbitMQ, Nginx, Jaeger and Postgres (@nikita-volkov, https://github.com/testcontainers/testcontainers-hs/pull/72)
+
 ## 0.5.3.0 -- 2026-03-31
 
 * Removed dependency on `optics-core` and `aeson-optics` (@alexbiehl, https://github.com/testcontainers/testcontainers-hs/pull/70)
diff --git a/src/TestContainers/Docker.hs b/src/TestContainers/Docker.hs
--- a/src/TestContainers/Docker.hs
+++ b/src/TestContainers/Docker.hs
@@ -670,11 +670,24 @@
 -- @since 0.5.0.0
 createRyukReaper :: TestContainer Reaper
 createRyukReaper = do
-  dockerSocketLocation <-
-    liftIO $
-      lookupEnv "DOCKER_HOST"
-        <&> (>>= stripPrefix "unix://")
-        <&> fromMaybe "/var/run/docker.sock"
+  -- Ryuk needs access to the Docker daemon socket to reap resources. The way
+  -- that socket is exposed depends on whether the daemon runs Linux or Windows
+  -- containers. Docker automatically selects the matching ryuk image variant
+  -- from the multi-arch manifest based on the daemon's OS.
+  dockerSocketMount <- do
+    onLinux <- isDockerOnLinux
+    if onLinux
+      then do
+        dockerSocketLocation <-
+          liftIO $
+            lookupEnv "DOCKER_HOST"
+              <&> (>>= stripPrefix "unix://")
+              <&> fromMaybe "/var/run/docker.sock"
+        pure (pack dockerSocketLocation, "/var/run/docker.sock")
+      else
+        -- Windows containers talk to the daemon through the named pipe rather
+        -- than a unix socket. This is what the windows/amd64 ryuk build expects.
+        pure ("\\\\.\\pipe\\docker_engine", "\\\\.\\pipe\\docker_engine")
   ryukContainer <-
     run $
       containerRequest (fromTag ryukImageTag)
@@ -682,9 +695,14 @@
         -- Ryuk destroys itself once it reaped the resources,
         -- no need to register itself with itself.
         withoutReaper
-        & setVolumeMounts [(pack dockerSocketLocation, "/var/run/docker.sock")]
+        & setVolumeMounts [dockerSocketMount]
         & setExpose [ryukPort]
-        & setWaitingFor (waitUntilMappedPortReachable ryukPort)
+        -- Wait for ryuk's own startup log rather than probing its port.
+        -- A TCP probe (waitUntilMappedPortReachable) registers as a client
+        -- that immediately disconnects without sending any labels; ryuk 0.14.0
+        -- treats this as a completed session and exits with its 1 ns
+        -- reconnection timeout, so the subsequent real connection gets RST.
+        & setWaitingFor (waitForLogLine Stdout ("Started" `LazyText.isInfixOf`))
         & setRm True
 
   let (ryukContainerAddress, ryukContainerPort) =
@@ -786,11 +804,20 @@
 fromTag :: ImageTag -> ToImage
 fromTag tag = defaultToImage $ do
   tracer <- askTracer
-  output <- docker tracer ["pull", "--quiet", tag]
-  return $
-    Image
-      { tag = strip (pack output)
-      }
+  pullWithRetry tracer 3
+  where
+    pull tracer = do
+      output <- docker tracer ["pull", "--quiet", tag]
+      pure $ Image {tag = strip (pack output)}
+    pullWithRetry :: Tracer -> Int -> TestContainer Image
+    pullWithRetry tracer 0 = pull tracer
+    pullWithRetry tracer n = do
+      result <- try (pull tracer)
+      case result of
+        Right image -> pure image
+        Left (_ :: DockerException) -> do
+          liftIO (threadDelay 2000000)
+          pullWithRetry tracer (n - 1)
 
 -- | Get an `Image` from an image id. This doesn't run @docker pull@ or any other Docker command
 -- on construction.
@@ -1104,9 +1131,13 @@
 waitUntilReady :: Container -> WaitUntilReady -> TestContainer ()
 waitUntilReady container@Container {id} input = do
   Config {configDefaultWaitTimeout} <- ask
-  interpreter $ case configDefaultWaitTimeout of
-    Just seconds -> waitUntilTimeout seconds input
-    Nothing -> input
+  interpreter $ case input of
+    WaitUntilTimeout {} ->
+      input
+    _ ->
+      case configDefaultWaitTimeout of
+        Just seconds -> waitUntilTimeout seconds input
+        Nothing -> input
   where
     interpreter :: WaitUntilReady -> TestContainer ()
     interpreter wait =
diff --git a/src/TestContainers/Docker/Reaper.hs b/src/TestContainers/Docker/Reaper.hs
--- a/src/TestContainers/Docker/Reaper.hs
+++ b/src/TestContainers/Docker/Reaper.hs
@@ -2,6 +2,7 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 module TestContainers.Docker.Reaper
   ( Reaper (..),
@@ -14,6 +15,8 @@
   )
 where
 
+import Control.Concurrent (threadDelay)
+import Control.Exception (IOException, try)
 import Control.Monad (replicateM)
 import Control.Monad.Trans.Resource (MonadResource, allocate)
 import Data.Text (Text, pack, unpack)
@@ -58,7 +61,7 @@
 -- @since 0.5.0.0
 ryukImageTag :: Text
 ryukImageTag =
-  "docker.io/testcontainers/ryuk:0.3.4"
+  "docker.io/testcontainers/ryuk:0.14.0"
 
 -- | Exposed port for the ryuk reaper.
 --
@@ -84,24 +87,12 @@
   (_releaseKey, (_socket, ryuk)) <-
     allocate
       ( do
-          let hints =
-                Socket.defaultHints {Socket.addrSocketType = Socket.Stream}
-          address <-
-            head <$> Socket.getAddrInfo (Just hints) (Just (unpack host)) (Just (show port))
-          socket <-
-            Socket.socket
-              (Socket.addrFamily address)
-              (Socket.addrSocketType address)
-              (Socket.addrProtocol address)
-          Socket.connect socket (Socket.addrAddress address)
-
-          -- Construct the reaper and regiter the session with it.
-          -- Doing it here intead of in the teardown (like we did before)
+          socket <- connectWithRetry 60
+          -- Construct the reaper and register the session with it.
+          -- Doing it here instead of in the teardown (like we did before)
           -- guarantees the Reaper knows about our session.
-          let reaper =
-                newReaper sessionId (Ryuk socket)
+          let reaper = newReaper sessionId (Ryuk socket)
           register reaper sessionIdLabel sessionId
-
           pure (socket, reaper)
       )
       ( \(socket, _ryuk) -> do
@@ -111,6 +102,29 @@
       )
 
   pure ryuk
+  where
+    -- On macOS (Docker Desktop / Colima), port-forwarding from the VM to the
+    -- host can lag a moment behind the container's own "Started" log line.
+    -- Retry the connect rather than failing immediately.
+    connectWithRetry :: Int -> IO Socket.Socket
+    connectWithRetry 0 = do
+      let hints = Socket.defaultHints {Socket.addrSocketType = Socket.Stream}
+      address <- head <$> Socket.getAddrInfo (Just hints) (Just (unpack host)) (Just (show port))
+      socket <- Socket.socket (Socket.addrFamily address) (Socket.addrSocketType address) (Socket.addrProtocol address)
+      Socket.connect socket (Socket.addrAddress address)
+      pure socket
+    connectWithRetry attemptsLeft = do
+      let hints = Socket.defaultHints {Socket.addrSocketType = Socket.Stream}
+      address <- head <$> Socket.getAddrInfo (Just hints) (Just (unpack host)) (Just (show port))
+      socket <- Socket.socket (Socket.addrFamily address) (Socket.addrSocketType address) (Socket.addrProtocol address)
+      result <- try (Socket.connect socket (Socket.addrAddress address))
+      case result of
+        Right () ->
+          pure socket
+        Left (_ :: IOException) -> do
+          Socket.close socket
+          threadDelay 500000
+          connectWithRetry (attemptsLeft - 1)
 
 newReaper ::
   -- | Session id
diff --git a/src/TestContainers/Image.hs b/src/TestContainers/Image.hs
--- a/src/TestContainers/Image.hs
+++ b/src/TestContainers/Image.hs
@@ -23,21 +23,21 @@
 -- | Image for Redis database.
 --
 -- @
--- redis = fromTag "redis:5.0"
+-- redis = fromTag "redis:7.4"
 -- @
 --
 -- @since 0.1.0.0
 redis :: ToImage
 redis =
-  fromTag "redis:5.0"
+  fromTag "redis:7.4"
 
 -- | Image for Mongo database.
 --
 -- @
--- mongo = Tag "mongo:4.0.17"
+-- mongo = Tag "mongo:7.0"
 -- @
 --
 -- @since 0.1.0.0
 mongo :: ToImage
 mongo =
-  fromTag "mongo:4.0.17"
+  fromTag "mongo:7.0"
diff --git a/test/TestContainers/TastySpec.hs b/test/TestContainers/TastySpec.hs
--- a/test/TestContainers/TastySpec.hs
+++ b/test/TestContainers/TastySpec.hs
@@ -3,7 +3,7 @@
 
 module TestContainers.TastySpec (main, test_all) where
 
-import Data.Text.Lazy (isInfixOf)
+import qualified Data.Text.Lazy as LazyText
 import Test.Tasty (TestTree, defaultMain, testGroup)
 import Test.Tasty.HUnit
 import TestContainers.Tasty
@@ -51,19 +51,17 @@
 
   _rabbitmq <-
     run $
-      containerRequest (fromTag "rabbitmq:3.8.4")
+      containerRequest (fromTag "rabbitmq:3.13")
         & setRm False
         & setExpose [5672]
         & withNetwork net
         & withFollowLogs consoleLogConsumer
         & setWaitingFor
-          ( waitForLogLine Stdout (("completed with" `isInfixOf`))
-              <> waitUntilMappedPortReachable 5672
-          )
+          (waitUntilTimeout 300 (waitForLogLine Stdout ("started TCP listener" `LazyText.isInfixOf`)))
 
   _nginx <-
     run $
-      containerRequest (fromTag "nginx:1.23.1-alpine")
+      containerRequest (fromTag "nginx:1.27-alpine")
         & setExpose [80]
         & withNetwork net
         & setWaitingFor
@@ -73,7 +71,7 @@
 
   _jaeger <-
     run $
-      containerRequest (fromTag "jaegertracing/all-in-one:1.6")
+      containerRequest (fromTag "jaegertracing/all-in-one:1.62.0")
         & setExpose ["5775/udp", "6831/udp", "6832/udp", "5778", "16686/tcp"]
         & withNetwork net
         & setWaitingFor
@@ -81,7 +79,7 @@
 
   _postgres <-
     run $
-      containerRequest (fromTag "postgres:16-alpine")
+      containerRequest (fromTag "postgres:17-alpine")
         & withCopyFileToContainer "test/data/init-script.sql" "/docker-entrypoint-initdb.d/"
 
   _helloWorld <-
diff --git a/testcontainers.cabal b/testcontainers.cabal
--- a/testcontainers.cabal
+++ b/testcontainers.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               testcontainers
-version:            0.5.3.0
+version:            0.5.4.0
 synopsis:           Docker containers for your integration tests.
 description:
   testcontainers is a Haskell library that provides a friendly API to
@@ -12,7 +12,7 @@
 license-file:       LICENSE
 author:             Alex Biehl
 maintainer:         alex.biehl@gmail.com
-copyright:          2023 Alex Biehl
+copyright:          2026 Alex Biehl
 category:           Development
 build-type:         Simple
 extra-source-files:
@@ -21,7 +21,7 @@
   test/data/init-script.sql
 
 tested-with:
-  GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.4 || ==9.4.2 || ==9.8.2
+  GHC ==9.2 || ==9.12
 
 source-repository head
   type:     git
@@ -86,7 +86,7 @@
     , hspec           >=2.0   && <3.0
     , tasty
     , tasty-discover  >=4.2.1 && <6
-    , tasty-hspec
+    , tasty-hspec     >=1.1.3 && <1.3
     , tasty-hunit
     , testcontainers
     , text
