diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## [2.8.0](https://github.com/haskell-github-trust/effectful-cli/compare/2.7.0...2.8.0) (2026-11-09)
+
+### Features
+
+* show all config in show command ([a9318f7](https://github.com/haskell-github-trust/effectful-cli/commit/a9318f7640627135fc8991f7bd5c835bcc47c55b))
+
 ## [2.7.0](https://github.com/haskell-github-trust/effectful-cli/compare/2.6.0...2.7.0) (2026-11-07)
 
 ### Features
diff --git a/effectful-cli.cabal b/effectful-cli.cabal
--- a/effectful-cli.cabal
+++ b/effectful-cli.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           effectful-cli
-version:        0.2.7.0
+version:        0.2.8.0
 synopsis:       CLI helpers for the effectful ecosystem
 description:    Please see the README at https://github.com/haskell-github-trust/effectful-cli
 category:       CLI
diff --git a/src/Data/CLI/Config.hs b/src/Data/CLI/Config.hs
--- a/src/Data/CLI/Config.hs
+++ b/src/Data/CLI/Config.hs
@@ -14,19 +14,24 @@
 import Data.Kind (Type)
 import Data.Text (Text)
 import Data.Text qualified as T
-import Effectful.CLI.Config (getConfigKey, readConfigText, setConfigKey)
+import Effectful.CLI.Config (getConfigKey, setConfigKey, showConfig)
 import Effectful.CLI.Exception (rethrowIO)
 import Effectful.CLI.IO (printOutputLn)
 import Effectful.Reader.Static (asks)
 import GHC.Enum (boundedEnumFrom, boundedEnumFromThen)
 import Options.Applicative (Parser, completer, listCompleter, metavar, strArgument)
-import Toml.Schema (FromValue)
+import Toml.Schema (FromValue, ToTable)
 
 -- | Application config @c@ that knows its config file and the type it decodes to.
 --
+-- 'ToTable' is needed by 'ConfigShow'; defaults come from the 'FromValue'
+-- instance filling missing keys.
+--
 -- @since 0.2.6.0
-class FromValue (ConfigOf c) => ConfigFile c where
+class (FromValue (ConfigOf c), ToTable (ConfigOf c)) => ConfigFile c where
   -- | Type the config file decodes to; 'ConfigSet' refuses to write anything else.
+  --
+  -- @since 0.2.8.0
   type ConfigOf c :: Type
 
   -- | Path of the config file.
@@ -42,7 +47,7 @@
 --
 -- @since 0.2.6.0
 data ConfigCommand c e
-  = -- | Print the config file.
+  = -- | Print the effective config: file values over the defaults.
     ConfigShow
   | -- | Print a dotted key's value.
     ConfigGet Text
@@ -72,7 +77,7 @@
   cmdname (ConfigGet _) = "get"
   cmdname (ConfigSet _ _) = "set"
 
-  doc ConfigShow = "Print the config file"
+  doc ConfigShow = "Print the effective config (file merged over defaults)"
   doc (ConfigGet _) = "Print a dotted key's value (e.g. section.key)"
   doc (ConfigSet _ _) = "Set a dotted key to a TOML literal, or a string if not one"
 
@@ -83,7 +88,7 @@
   runner cmd = do
     path <- asks @c configFilePath
     rethrowIO (InputError . ("config file: " <>) . T.show) $ case cmd of
-      ConfigShow -> printOutputLn . T.stripEnd =<< readConfigText path
+      ConfigShow -> printOutputLn =<< showConfig @(ConfigOf c) path
       ConfigGet k -> printOutputLn =<< getConfigKey path k
       ConfigSet k v -> setConfigKey @(ConfigOf c) (configKeys @c) path k v
 
diff --git a/src/Effectful/CLI/Config.hs b/src/Effectful/CLI/Config.hs
--- a/src/Effectful/CLI/Config.hs
+++ b/src/Effectful/CLI/Config.hs
@@ -4,6 +4,7 @@
 -- | Read and edit a TOML config file by dotted key (@github.project.team@).
 module Effectful.CLI.Config (
   readConfigText,
+  showConfig,
   getConfigKey,
   setConfigKey,
   lookupKey,
@@ -33,7 +34,7 @@
  )
 import Toml qualified
 import Toml.Pretty (prettyValue)
-import Toml.Schema (FromValue)
+import Toml.Schema (FromValue, ToTable (toTable))
 
 -- | The config file's contents, empty if it does not exist.
 --
@@ -45,6 +46,23 @@
 readConfigText path = liftIO $ do
   exists <- doesFileExist path
   if exists then T.readFile path else pure ""
+
+-- | The config file decoded as @a@ (so defaults fill missing keys, and a
+-- missing file is all defaults), rendered back as TOML.
+--
+-- @since 0.2.8.0
+showConfig
+  :: forall a es
+   . FromValue a
+  => ToTable a
+  => IOE :> es
+  => Error CLIError :> es
+  => FilePath
+  -> Eff es Text
+showConfig path =
+  readConfigText path >>= \raw -> case Toml.decode @a raw of
+    Failure es -> throwError (InputError ("invalid config: " <> T.stripEnd (T.pack (unlines es))))
+    Success _ a -> pure (T.stripEnd (T.show (prettyToml (toTable a))))
 
 -- | Render a dotted key's value: strings unquoted, tables as TOML sections.
 --
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -12,9 +12,17 @@
  )
 import Data.Error.CLI (CLIError (..), displayCLIError)
 import Data.List (isInfixOf)
+import Data.Maybe (fromMaybe)
 import Effectful (Eff, IOE, liftIO, runEff)
 import Effectful.CLI.Conc (batchConcurrentlyWithIndex)
-import Effectful.CLI.Config (getConfigKey, lookupKey, parseValue, setConfigKey, setKey)
+import Effectful.CLI.Config (
+  getConfigKey,
+  lookupKey,
+  parseValue,
+  setConfigKey,
+  setKey,
+  showConfig,
+ )
 import Effectful.CLI.Exception (rethrowIO)
 import Effectful.CLI.IO (resolveIndex)
 import Effectful.Concurrent (runConcurrent)
@@ -25,7 +33,18 @@
 import Test.Hspec
 import Text.Megaparsec.Char (string)
 import Toml (Table, Table' (..), Value' (..))
-import Toml.Schema (FromValue (..), parseTableFromValue, reqKey, reqKeyOf)
+import Toml.Schema (
+  FromValue (..),
+  ToTable (..),
+  ToValue (..),
+  defaultTableToValue,
+  optKey,
+  parseTableFromValue,
+  reqKey,
+  reqKeyOf,
+  table,
+  (.=),
+ )
 
 isInputError :: Either CLIError a -> Bool
 isInputError (Left (InputError _)) = True
@@ -151,6 +170,28 @@
       r `shouldSatisfy` \case
         Left (InputError m) -> "sec is a section, set one of: sec.x, sec.y" `isInfixOf` show m
         _ -> False
+
+    it "shows defaults for a missing file and file values over them" $ do
+      dir <- getTemporaryDirectory
+      let missing = dir </> "effectful-cli-config-missing.toml"
+          present = dir </> "effectful-cli-config-show.toml"
+      writeFile present "n = 3\n"
+      r <-
+        runEff . runErrorNoCallStack @CLIError $
+          (,) <$> showConfig @Defaulted missing <*> showConfig @Defaulted present
+      removeFile present
+      r `shouldBe` Right ("n = 7", "n = 3")
+
+newtype Defaulted = Defaulted Int
+
+instance FromValue Defaulted where
+  fromValue = parseTableFromValue (Defaulted . fromMaybe 7 <$> optKey "n")
+
+instance ToValue Defaulted where
+  toValue = defaultTableToValue
+
+instance ToTable Defaulted where
+  toTable (Defaulted n) = table ["n" .= n]
 
 newtype Section = Section Int
 
