diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,19 @@
 
 `hslua-aeson` uses [PVP Versioning][].
 
+## hslua-aeson-2.3.1
+
+Released 2024-01-18.
+
+-   Relaxed upper bound for aeson. This required changes to the
+    testsuite: The arbitrary JSON values produced by current aeson
+    versions include numbers that cannot be converted to Lua
+    numbers without loss of precision. Those are first converted
+    to representable numbers before round-tripping is tested.
+
+-   Relaxed upper bound for text, containers, and bytestring,
+    allowing text-2.1, containers-0.7, and bytestring-0.12.
+
 ## hslua-aeson-2.3.0.1
 
 Released 2023-03-13.
diff --git a/hslua-aeson.cabal b/hslua-aeson.cabal
--- a/hslua-aeson.cabal
+++ b/hslua-aeson.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.2
 name:                hslua-aeson
-version:             2.3.0.1
+version:             2.3.1
 synopsis:            Allow aeson data types to be used with Lua.
 description:         This package provides instances to push and receive any
                      datatype encodable as JSON to and from the Lua stack.
@@ -18,8 +18,10 @@
                    , GHC == 8.8.4
                    , GHC == 8.10.7
                    , GHC == 9.0.2
-                   , GHC == 9.2.5
-                   , GHC == 9.4.4
+                   , GHC == 9.2.8
+                   , GHC == 9.4.8
+                   , GHC == 9.6.3
+                   , GHC == 9.8.1
 
 source-repository head
   type:                git
@@ -29,16 +31,16 @@
 common common-options
   default-language:    Haskell2010
   build-depends:       base                 >= 4.11   && < 5
-                     , aeson                >= 1.5    && < 2.2
-                     , bytestring           >= 0.10.2 && < 0.12
-                     , containers           >= 0.5.9  && < 0.7
+                     , aeson                >= 1.5    && < 2.3
+                     , bytestring           >= 0.10.2 && < 0.13
+                     , containers           >= 0.5.9  && < 0.8
                      , hashable             >= 1.2    && < 1.5
                      , hslua-core           >= 2.0    && < 2.4
                      , hslua-marshalling    >= 2.1    && < 2.4
                      , mtl                  >= 2.2    && < 2.4
                      , scientific           >= 0.3    && < 0.4
                      , unordered-containers >= 0.2    && < 0.3
-                     , text                 >= 1.2    && < 2.1
+                     , text                 >= 1.2    && < 2.2
                      , vector               >= 0.7
   default-extensions:  BangPatterns
                      , CPP
diff --git a/src/HsLua/Aeson.hs b/src/HsLua/Aeson.hs
--- a/src/HsLua/Aeson.hs
+++ b/src/HsLua/Aeson.hs
@@ -1,6 +1,6 @@
 {-|
 Module      :  HsLua.Aeson
-Copyright   :  © 2017-2023 Albert Krewinkel
+Copyright   :  © 2017-2024 Albert Krewinkel
 License     :  MIT
 Maintainer  :  Albert Krewinkel <tarleb@hslua.org>
 
diff --git a/test/test-hslua-aeson.hs b/test/test-hslua-aeson.hs
--- a/test/test-hslua-aeson.hs
+++ b/test/test-hslua-aeson.hs
@@ -1,12 +1,13 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 {-|
-Copyright   :  © 2017-2023 Albert Krewinkel
+Copyright   :  © 2017-2024 Albert Krewinkel
 License     :  MIT
 
 Tests for Aeson–Lua glue.
 -}
 import Control.Monad (when)
 import Data.Aeson (ToJSON, object, (.=))
+import Data.Scientific (Scientific, fromFloatDigits, toRealFloat)
 import Data.Text (Text)
 import HsLua.Core as Lua
 import HsLua.Marshalling
@@ -25,10 +26,8 @@
 import qualified Data.Aeson.KeyMap as KeyMap
 #if !MIN_VERSION_aeson(2,0,3)
 import Data.Aeson.Key (Key, fromText)
-import Data.Scientific (Scientific, fromFloatDigits)
 #endif
 #else
-import Data.Scientific (Scientific, fromFloatDigits)
 import qualified Data.HashMap.Strict as KeyMap
 #endif
 
@@ -42,7 +41,7 @@
 tests = testGroup "hslua-aeson"
   [ testGroup "Value"
     [ testProperty "can be round-tripped through the stack" $
-      assertRoundtripEqual pushValue peekValue
+      assertRoundtripEqual pushValue peekValue . numbersToDoubles
     , testProperty "can roundtrip a bool nested in 50 layers of arrays" $
       \b -> QC.monadicIO $ do
         let go _ x = Aeson.Array $ Vector.fromList [x]
@@ -119,11 +118,22 @@
     failLua $ "peeking modified the stack: " ++ show afterPeekSize
   return result
 
--- aeson defines instances for Arbitrary since 2.0.3.0
-#if !MIN_VERSION_aeson(2,0,3)
+
+-- | Ensure that numbers are representable as Doubles.
+numbersToDoubles :: Aeson.Value -> Aeson.Value
+numbersToDoubles (Aeson.Number x) = Aeson.Number . luaNumberToScientific $
+                                    toRealFloat x
+numbersToDoubles (Aeson.Object x) = Aeson.Object $ fmap numbersToDoubles x
+numbersToDoubles (Aeson.Array x) = Aeson.Array $ fmap numbersToDoubles x
+numbersToDoubles x = x
+
+-- | Convert a Lua number to a scientific number, which are the basis for JSON
+-- numbers.
 luaNumberToScientific :: Lua.Number -> Scientific
 luaNumberToScientific = fromFloatDigits . (realToFrac :: Lua.Number -> Double)
 
+-- aeson defines instances for Arbitrary since 2.0.3.0
+#if !MIN_VERSION_aeson(2,0,3)
 instance Arbitrary Aeson.Value where
   arbitrary = arbitraryValue 9
 
