diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,11 @@
-## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.25.0.0...main)
+## [_Unreleased_](https://github.com/freckle/freckle-app/compare/freckle-app-v1.26.1.0...main)
+
+## [v1.26.1.0](https://github.com/freckle/freckle-app/compare/freckle-app-v1.26.0.0...freckle-app-v1.26.1.0)
+
+- Add `withPostgresPool` and `withPostgresPoolWith`, which create a
+  PostgreSQL pool and close it with `destroyAllResources` once the given
+  action completes. Prefer these to `makePostgresPool` and
+  `makePostgresPoolWith`, which leave the pool open for the caller to close.
 
 ## [v1.26.0.0](https://github.com/freckle/freckle-app/compare/freckle-app-v1.24.0.1...freckle-app-v1.25.0.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.18
 name:               freckle-app
-version:            1.26.0.0
+version:            1.26.1.0
 license:            MIT
 license-file:       LICENSE
 maintainer:         Freckle Education
diff --git a/library/Freckle/App.hs b/library/Freckle/App.hs
--- a/library/Freckle/App.hs
+++ b/library/Freckle/App.hs
@@ -116,18 +116,18 @@
 -- > instance HasStatsClient App where
 -- >   statsClientL = lens appStatsClient $ \x y -> x { appStatsClient = y }
 --
--- The @"Freckle.App.Database"@ module provides @'makePostgresPool'@ for
--- building a Pool given this (limited) config data:
+-- The @"Freckle.App.Database"@ module provides 'withPostgresPool' and 'withPostgresPoolWith' for
+-- building (and eventually destroying) a Pool given this (limited) config data:
 --
 -- > loadApp :: (App -> IO a) -> IO a
 -- > loadApp f = do
 -- >   appConfig{..} <- loadConfig
 -- >   withLogger configLoggerSettings $ \appLogger ->
--- >     appSqlPool <- runWithLogger appLogger $ makePostgresPool configDbPoolSize
--- >     withTracerProvider $ \tracerProvider -> do
--- >       withStatsClient configStatsSettings $ \appStatsClient -> do
--- >         let appTracer = makeTracer tracerProvider "my-app" tracerOptions
--- >         f App{..}
+-- >     runWithLogger appLogger $ withPostgresPoolWith configPostgresConfig $ \appSqlPool ->
+-- >       withTracerProvider $ \tracerProvider -> do
+-- >         withStatsClient configStatsSettings $ \appStatsClient -> do
+-- >           let appTracer = makeTracer tracerProvider "my-app" tracerOptions
+-- >           f App{..}
 --
 -- This unlocks @'runDB'@ for your application:
 --
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
@@ -23,6 +23,8 @@
     -- * Connection pools
   , HasSqlPool (..)
   , SqlPool
+  , withPostgresPool
+  , withPostgresPoolWith
   , makePostgresPool
   , makePostgresPoolWith
 
@@ -74,6 +76,7 @@
 import OpenTelemetry.Trace qualified as Trace
 import System.Process.Typed (proc, readProcessStdout_)
 import UnliftIO.Concurrent (threadDelay)
+import UnliftIO.Exception (bracket)
 import UnliftIO.IORef
 import Yesod.Core.Types (HandlerData (..), RunHandlerEnv (..))
 
@@ -88,6 +91,10 @@
 instance HasSqlPool site => HasSqlPool (HandlerData child site) where
   getSqlPool = getSqlPool . rheSite . handlerEnv
 
+-- | Create a PostgreSQL pool from the environment
+--
+-- Prefer 'withPostgresPool', which closes the pool with
+-- 'destroyAllResources' once the given action completes.
 makePostgresPool :: (MonadUnliftIO m, MonadLoggerIO m) => m SqlPool
 makePostgresPool = do
   conf <- liftIO $ do
@@ -95,6 +102,11 @@
     Env.parse id $ envParseDatabaseConf postgresPasswordSource
   makePostgresPoolWith conf
 
+-- | Create a PostgreSQL pool, closing it once the given action completes
+withPostgresPool
+  :: (MonadUnliftIO m, MonadLoggerIO m) => (SqlPool -> m a) -> m a
+withPostgresPool = bracket makePostgresPool (liftIO . destroyAllResources)
+
 -- | Run a Database action with connection stats and tracing
 --
 -- This uses OpenTelemetry and 'MonadTracer'. For callstacks in traces to be
@@ -298,6 +310,10 @@
         (Only timeoutMillis)
     for_ pccSchema $ \schema -> execute conn [sql| SET search_path TO ? |] (Only schema)
 
+-- | Create a PostgreSQL pool for the given connection configuration
+--
+-- Prefer 'withPostgresPoolWith', which closes the pool with
+-- 'destroyAllResources' once the given action completes.
 makePostgresPoolWith
   :: (MonadUnliftIO m, MonadLoggerIO m) => PostgresConnectionConf -> m SqlPool
 makePostgresPoolWith conf@PostgresConnectionConf {..} = case pccPassword of
@@ -307,6 +323,16 @@
       (setStartupOptions conf)
       (postgresConnectionString conf password)
       pccPoolSize
+
+-- | Create a PostgreSQL pool for the given connection configuration, closing
+-- it once the given action completes
+withPostgresPoolWith
+  :: (MonadUnliftIO m, MonadLoggerIO m)
+  => PostgresConnectionConf
+  -> (SqlPool -> m a)
+  -> m a
+withPostgresPoolWith conf =
+  bracket (makePostgresPoolWith conf) (liftIO . destroyAllResources)
 
 -- | Creates a PostgreSQL pool using IAM auth for the password
 makePostgresPoolWithIamAuth
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
 name: freckle-app
-version: 1.26.0.0
+version: 1.26.1.0
 
 maintainer: Freckle Education
 category: Utils
