diff --git a/bob/extension/config.py b/bob/extension/config.py
index 9919b3e23b9d5d496ef28dca2420795054f67027..aa85125c0ba151e36cbad0c07ef677bffc5f300b 100644
--- a/bob/extension/config.py
+++ b/bob/extension/config.py
@@ -5,10 +5,12 @@
 '''
 
 import imp
-import pkg_resources
 import pkgutil
+import time
 from os.path import isfile
 import logging
+import pkg_resources
+import click
 
 logger = logging.getLogger(__name__)
 
@@ -211,3 +213,25 @@ def mod_to_context(mod):
   """
   return {k: v for k, v in mod.__dict__.items()
           if not (k.startswith('__') and k.endswith('__'))}
+
+
+def dump_config(params, ctx):
+  config_file = open(ctx.params.get('dump_config'), 'w')
+  logger.debug("Generating configuration file `%s'...", config_file)
+  config_file.write('## Configuration file automatically generated at %s '
+                    'for %s.\n\n\n' % (time.strftime("%d/%m/%Y"),
+                                 ctx.command_path))
+  for param in params:
+    if param.name not in ctx.params or param.name == 'dump_config':
+      continue
+    if not isinstance(param, click.Option):
+      continue
+    config_file.write('## %s.\n' % param.help)
+    config_file.write(
+      '## Option: %s [default: %s]\n' % (
+        ', '.join(param.opts), str(param.default)
+      )
+    )
+    config_file.write('# %s = %s\n\n' % (param.name,
+                                     str(ctx.params[param.name])))
+    config_file.write('\n\n\n')
diff --git a/bob/extension/scripts/click_helper.py b/bob/extension/scripts/click_helper.py
index a48411e389795aa36fddace546f5a4b2dd7a0604..4790ebabfc350ad7659be7e58581f05da3a4fabd 100644
--- a/bob/extension/scripts/click_helper.py
+++ b/bob/extension/scripts/click_helper.py
@@ -1,6 +1,5 @@
 from ..log import set_verbosity_level
-from ..config import load, mod_to_context
-import time
+from ..config import load, mod_to_context, dump_config
 import click
 import logging
 
@@ -205,30 +204,10 @@ class ConfigCommand(click.Command):
           # make sure to set this back to False for future invocations
           param.required = False
     if ctx.params.get('dump_config') is not None:
-      self.dump_config(ctx)
+      dump_config(self.params, ctx)
 
     return super(ConfigCommand, self).invoke(ctx)
 
-  def dump_config(self, ctx):
-    config_file = open(ctx.params.get('dump_config'), 'w')
-    config_file.write('## Configuration file automatically generated at %s '
-                      'for %s.\n\n\n' % (time.strftime("%d/%m/%Y"),
-                                   ctx.command_path))
-    for param in self.params:
-      if param.name not in ctx.params or param.name == 'dump_config':
-        continue
-      if not isinstance(param, click.Option):
-          continue
-      config_file.write('## %s.\n' % param.help)
-      config_file.write(
-          '## Option: %s [default: %s]\n' % (
-              ', '.join(param.opts), str(param.default)
-          )
-      )
-      config_file.write('# %s = %s\n\n' % (param.name,
-                                       str(ctx.params[param.name])))
-      config_file.write('\n\n\n')
-
 
 class ResourceOption(click.Option):
   """A click.Option that is aware if the user actually provided this option
diff --git a/doc/py_api.rst b/doc/py_api.rst
index f57055b5d16aaffac88eea3e8f58c83ea7bae5c6..a1f2d23ed92f1944c80030140c422ee968d1cbcd 100644
--- a/doc/py_api.rst
+++ b/doc/py_api.rst
@@ -58,6 +58,7 @@ Configuration
     bob.extension.rc_config.ENVNAME
     bob.extension.rc_config.RCFILENAME
     bob.extension.config.load
+    bob.extension.config.dump_config
 
 Stacked Processors
 ^^^^^^^^^^^^^^^^^^