diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,17 @@
-## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.20.3.0...main)
+## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.21.0.0...main)
+
+## [v1.21.0.0](https://github.com/freckle/freckle-app/compare/freckle-app-v1.20.3.0...freckle-app-v1.21.0.0)
+
+The following have moved from `Freckle.App.Database` to a new package, `persistent-sql-lifted`:
+
+- `MonadSqlTx (..)`
+- `HasSqlBackend (..)`
+- `MonadSqlBackend (..)`
+- `liftSql`
+
+The definitions in `freckle-app` have been replaced with reëxports,
+so no code needs to change for users, except that a dependency on
+the `persistent-sql-lifted` package is now required.
 
 ## [v1.20.3.0](https://github.com/freckle/freckle-app/compare/freckle-app-v1.20.2.1...freckle-app-v1.20.3.0)
 
diff --git a/freckle-app.cabal b/freckle-app.cabal
--- a/freckle-app.cabal
+++ b/freckle-app.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.22
 name:               freckle-app
-version:            1.20.3.0
+version:            1.21.0.0
 license:            MIT
 license-file:       LICENSE
 maintainer:         Freckle Education
@@ -142,6 +142,7 @@
         path-pieces >=0.2.1,
         persistent >=2.13.3.5,
         persistent-postgresql >=2.13.5.2,
+        persistent-sql-lifted >=1.20.3.0,
         postgresql-simple >=0.6.4,
         primitive >=0.7.3.0,
         resource-pool >=0.4.0.0,
diff --git a/library/Freckle/App/Database.hs b/library/Freckle/App/Database.hs
--- a/library/Freckle/App/Database.hs
+++ b/library/Freckle/App/Database.hs
@@ -56,6 +56,7 @@
   , runSqlPool
   , runSqlPoolWithExtensibleHooks
   )
+import Database.Persist.Sql.Lifted
 import Database.Persist.SqlBackend.Internal.SqlPoolHooks (SqlPoolHooks (..))
 import Database.Persist.SqlBackend.SqlPoolHooks
 import Database.PostgreSQL.Simple
@@ -67,7 +68,6 @@
 import Database.PostgreSQL.Simple.SqlQQ (sql)
 import Freckle.App.Env (Timeout (..))
 import Freckle.App.Env qualified as Env
-import Freckle.App.Exception.MonadUnliftIO
 import Freckle.App.OpenTelemetry
 import Freckle.App.Stats (HasStatsClient)
 import Freckle.App.Stats qualified as Stats
@@ -77,32 +77,6 @@
 import UnliftIO.Concurrent (threadDelay)
 import UnliftIO.IORef
 import Yesod.Core.Types (HandlerData (..), RunHandlerEnv (..))
-
--- | A monadic context in which a SQL backend is available
---   for running database queries
-class MonadUnliftIO m => MonadSqlBackend m where
-  getSqlBackendM :: m SqlBackend
-
-instance (HasSqlBackend r, MonadUnliftIO m) => MonadSqlBackend (ReaderT r m) where
-  getSqlBackendM = asks getSqlBackend
-
--- | Generalize from 'SqlPersistT' to 'MonadSqlBackend'
-liftSql :: (MonadSqlBackend m, HasCallStack) => ReaderT SqlBackend m a -> m a
-liftSql (ReaderT f) = checkpointCallStack $ getSqlBackendM >>= f
-
--- | The constraint @'MonadSqlTx' db m@ indicates that @m@ is a monadic
---   context that can run @db@ actions, usually as a SQL transaction.
---   Typically, this means that @db@ needs a connection and @m@ can
---   provide one, e.g. from a connection pool.
-class (MonadSqlBackend db, MonadUnliftIO m) => MonadSqlTx db m | m -> db where
-  -- | Runs the action in a SQL transaction
-  runSqlTx :: HasCallStack => db a -> m a
-
-class HasSqlBackend a where
-  getSqlBackend :: a -> SqlBackend
-
-instance HasSqlBackend SqlBackend where
-  getSqlBackend = id
 
 type SqlPool = Pool SqlBackend
 
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
 name: freckle-app
-version: 1.20.3.0
+version: 1.21.0.0
 
 maintainer: Freckle Education
 category: Utils
@@ -124,6 +124,7 @@
     - openapi3
     - path-pieces
     - persistent
+    - persistent-sql-lifted
     - persistent-postgresql
     - postgresql-simple
     - primitive
