diff --git a/wcs.cfg-sample b/wcs.cfg-sample
new file mode 100644
index 0000000..a90f401
--- /dev/null
+++ b/wcs.cfg-sample
@@ -0,0 +1,7 @@
+[main]
+app_dir = /var/lib/wcs
+data_dir = /usr/share/wcs
+auto_create_vhosts = true
+use_long_traces = true
+#error_log = /var/log/wcs/error.log
+#redirect_on_unknown_vhost = http://www.mysite.com
diff --git a/wcs/ctl/apply_timeouts.py b/wcs/ctl/apply_timeouts.py
deleted file mode 100644
index 6f96a80..0000000
--- a/wcs/ctl/apply_timeouts.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# w.c.s. - web application for online forms
-# Copyright (C) 2005-2010  Entr'ouvert
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301  USA
-
-from qommon.ctl import Command, make_option
-
-class CmdApplyTimeouts(Command):
-    name = 'apply_timeouts'
-
-    def __init__(self):
-        Command.__init__(self, [
-                make_option('--extra', metavar='DIR', action='append',
-                            dest='extra', default=[]),
-                make_option('--app-dir', metavar='DIR', action='store',
-                            dest='app_dir', default=None),
-                make_option('--data-dir', metavar='DIR', action='store',
-                            dest='data_dir', default=None),
-                make_option('--silent', action='store_true',
-                            dest='silent', default=False),
-                ])
-
-    def execute(self, options, args):
-        import wcs.workflows as workflows
-        import publisher
-
-        if options.app_dir:
-            publisher.WcsPublisher.APP_DIR = options.app_dir
-        if options.data_dir:
-            publisher.WcsPublisher.DATA_DIR = options.data_dir
-        if options.silent:
-            sys.stdout = file('/dev/null', 'w')
-            sys.stderr = file('/dev/null', 'w')
-
-        pub = publisher.WcsPublisher.create_publisher()
-        workflows.apply_timeouts()
-
-CmdApplyTimeouts.register()
diff --git a/wcs/ctl/clean_sessions.py b/wcs/ctl/clean_sessions.py
index 43d2ade..13001ec 100644
--- a/wcs/ctl/clean_sessions.py
+++ b/wcs/ctl/clean_sessions.py
@@ -40,9 +40,10 @@ def clean_vhost_sessions():
 class CmdCleanSessions(Command):
     name = 'clean_sessions'
 
-    def execute(self, options, args):
+    def execute(self, base_options, sub_options, args):
         import publisher
 
+        publisher.WcsPublisher.configure(self.config, sub_options.extra)
         pub = publisher.WcsPublisher.create_publisher()
 
         app_dir = pub.app_dir
diff --git a/wcs/ctl/export_settings.py b/wcs/ctl/export_settings.py
index b758a1a..af62622 100644
--- a/wcs/ctl/export_settings.py
+++ b/wcs/ctl/export_settings.py
@@ -30,11 +30,12 @@ class CmdExportSettings(Command):
                             dest='vhost'),
                 ])
 
-    def execute(self, options, args):
+    def execute(self, base_options, sub_options, args):
         import publisher
         stdout = sys.stdout
+        publisher.WcsPublisher.configure(self.config, sub_options.extra)
         pub = publisher.WcsPublisher.create_publisher()
-        pub.app_dir = os.path.join(pub.app_dir, options.vhost)
+        pub.app_dir = os.path.join(pub.app_dir, sub_options.vhost)
         pub.reload_cfg()
         print >> stdout, pub.export_cfg()
 
diff --git a/wcs/ctl/process_bounce.py b/wcs/ctl/process_bounce.py
index 5fa8388..d14f125 100644
--- a/wcs/ctl/process_bounce.py
+++ b/wcs/ctl/process_bounce.py
@@ -31,7 +31,7 @@ COMMA_SPACE = ', '
 class CmdProcessBounce(Command):
     name = 'process_bounce'
 
-    def execute(self, options, args):
+    def execute(self, base_options, sub_options, args):
         from qommon.tokens import Token
         from qommon.bounces import Bounce
 
@@ -59,6 +59,7 @@ class CmdProcessBounce(Command):
             except (IndexError, KeyError):
                 return
 
+            publisher.WcsPublisher.configure(self.config, sub_options.extra)
             pub = publisher.WcsPublisher.create_publisher()
             pub.app_dir = os.path.join(pub.app_dir, server_part)
 
diff --git a/wcs/ctl/rebuild_indexes.py b/wcs/ctl/rebuild_indexes.py
index 7bc3b4d..b5a5586 100644
--- a/wcs/ctl/rebuild_indexes.py
+++ b/wcs/ctl/rebuild_indexes.py
@@ -42,25 +42,21 @@ class CmdRebuildIndexes(Command):
                             dest='all', default=False),
                 make_option('--destroy', action='store_true',
                             dest='destroy', default=False),
-                make_option('--extra', metavar='DIR', action='append',
-                            dest='extra', default=[]),
                 ])
 
-    def execute(self, options, args):
+    def execute(self, base_options, sub_options, args):
         import publisher
 
-        for directory in options.extra:
-            publisher.WcsPublisher.register_extra_dir(directory)
-
+        publisher.WcsPublisher.configure(self.config, sub_options.extra)
         pub = publisher.WcsPublisher.create_publisher()
 
         app_dir = pub.app_dir
-        if options.all:
+        if sub_options.all:
             hostnames = os.listdir(app_dir)
         else:
             hostnames = args
         for hostname in hostnames:
             pub.app_dir = os.path.join(app_dir, hostname)
-            rebuild_vhost_indexes(destroy=options.destroy)
+            rebuild_vhost_indexes(destroy=sub_options.destroy)
 
 CmdRebuildIndexes.register()
diff --git a/wcs/ctl/start.py b/wcs/ctl/start.py
index 7348040..9440ae3 100644
--- a/wcs/ctl/start.py
+++ b/wcs/ctl/start.py
@@ -30,43 +30,32 @@ class CmdStart(Command):
         Command.__init__(self, [
                 make_option('--port', metavar='PORT', action='store',
                             dest='port', default=3001),
-                make_option('--extra', metavar='DIR', action='append',
-                            dest='extra', default=[]),
                 make_option('--handler-connection-limit', metavar='LIMIT',
                             action='store',
                             dest='handler_connection_limit', default=None),
                 make_option('--script-name', metavar='NAME', action='store',
                             dest='script_name', default=None),
-                make_option('--app-dir', metavar='DIR', action='store',
-                            dest='app_dir', default=None),
-                make_option('--data-dir', metavar='DIR', action='store',
-                            dest='data_dir', default=None),
                 make_option('--http', action='store_true',
                             dest='http', default=False),
                 make_option('--silent', action='store_true',
                             dest='silent', default=False),
                 ])
 
-    def execute(self, options, args):
+    def execute(self, base_options, sub_options, args):
         import publisher
 
         run_kwargs = {}
-        run_kwargs['port'] = int(options.port)
+        run_kwargs['port'] = int(sub_options.port)
         run_kwargs['spawn_cron'] = True
         run_function = qommon.scgi_server.run
-        for directory in options.extra:
-            publisher.WcsPublisher.register_extra_dir(directory)
-        if options.handler_connection_limit:
-            run_kwargs['handler_connection_limit'] = int(options.handler_connection_limit)
-        if options.script_name:
-            run_kwargs['script_name'] = options.script_name
-        if options.app_dir:
-            publisher.WcsPublisher.APP_DIR = options.app_dir
-        if options.data_dir:
-            publisher.WcsPublisher.DATA_DIR = options.data_dir
-        if options.http:
+        publisher.WcsPublisher.configure(self.config, sub_options.extra)
+        if sub_options.handler_connection_limit:
+            run_kwargs['handler_connection_limit'] = int(sub_options.handler_connection_limit)
+        if sub_options.script_name:
+            run_kwargs['script_name'] = sub_options.script_name
+        if sub_options.http:
             run_function = qommon.scgi_server.http_run
-        if options.silent:
+        if sub_options.silent:
             sys.stdout = file('/dev/null', 'w')
             sys.stderr = file('/dev/null', 'w')
 
diff --git a/wcs/publisher.py b/wcs/publisher.py
index ae3400f..d2aadc5 100644
--- a/wcs/publisher.py
+++ b/wcs/publisher.py
@@ -89,6 +89,27 @@ class WcsPublisher(StubWcsPublisher):
         return admin
     get_admin_module = classmethod(get_admin_module)
 
+    def configure(cls, config, extra=[]):
+        for directory in extra:
+            cls.register_extra_dir(directory)
+
+        if config.has_option("main", "app_dir"):
+            cls.APP_DIR = config.get("main", "app_dir")
+        if config.has_option("main", "data_dir"):
+            cls.DATA_DIR = config.get("main", "data_dir")
+        if config.has_option("main", "error_log"):
+            cls.ERROR_LOG = config.get("main", "error_log")
+        if config.has_option("main", "use_long_traces"):
+            cls.USE_LONG_TRACES = config.getboolean("main",
+                    "use_long_traces")
+        if config.has_option("main", "auto_create_vhosts"):
+            cls.auto_create_appdir = config.getboolean("main",
+                    "auto_create_vhosts")
+        if config.has_option("main", "redirect_on_unknown_vhost"):
+            cls.missing_appdir_redirect = config.get("main",
+                    "redirect_on_unknown_vhost")
+    configure = classmethod(configure)
+
     def set_config(self, request = None):
         QommonPublisher.set_config(self, request = request)
         filename = os.path.join(self.app_dir, 'config.pck')
diff --git a/wcs/qommon/cron.py b/wcs/qommon/cron.py
index de2200f..7b20cf3 100644
--- a/wcs/qommon/cron.py
+++ b/wcs/qommon/cron.py
@@ -80,7 +80,6 @@ def spawn_cron(create_publisher):
     global cron_pid
     cron_pid = os.fork()
     if cron_pid == 0:
-        print >>sys.stderr, 'Cron process launched'
         pub = create_publisher()
         # set process name, this only works on Linux, 15 == PR_SET_NAME
         if dl:
diff --git a/wcs/qommon/ctl.py b/wcs/qommon/ctl.py
index f21aff3..4c152e7 100644
--- a/wcs/qommon/ctl.py
+++ b/wcs/qommon/ctl.py
@@ -16,6 +16,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301  USA
 
+import ConfigParser
 import optparse
 from optparse import make_option
 import sys
@@ -35,11 +36,32 @@ class Command:
     usage_args = '[ options ... ]'
 
     def __init__(self, options=[]):
-        self.options = options
+        self.config = ConfigParser.ConfigParser()
+        self.options = options + [
+                    make_option('--extra', metavar='DIR', action='append',
+                        dest='extra', default=[]),
+                    make_option('--app-dir', metavar='DIR', action='store',
+                        dest='app_dir', default=None),
+                    make_option('--data-dir', metavar='DIR', action='store',
+                        dest='data_dir', default=None),
+                    ]
 
-    def run(self, args):
-        options, args = self.parse_args(args)
-        return self.execute(options, args)
+    def run(self, args, base_options):
+        if base_options.configfile:
+            try:
+                self.config.read(base_options.configfile)
+            except ConfigParser.ParsingError, e:
+                print >> sys.stderr, 'Invalid configuration file %s' % base_options.configfile
+                print >> sys.stderr, e
+                print >> sys.stderr, 'Using default options'
+        if not self.config.has_section('main'):
+            self.config.add_section('main')
+        sub_options, args = self.parse_args(args)
+        if sub_options.app_dir:
+            self.config.set("main", "app_dir", sub_options.app_dir)
+        if sub_options.data_dir:
+            self.config.set("main", "data_dir", sub_options.data_dir)
+        return self.execute(base_options, sub_options, args)
 
     def parse_args(self, args):
         self.parser = optparse.OptionParser(
@@ -48,7 +70,7 @@ class Command:
         self.parser.add_options(self.options)
         return self.parser.parse_args(args)
 
-    def execute(self, options, args):
+    def execute(self, base_options, sub_options, args):
         """The body of the command"""
         raise NotImplementedError
 
@@ -103,6 +125,8 @@ class Ctl:
 
     def run(self, args):
         options, args = self.parser.parse_args(args)
+        if not args:
+            self.parser.error("You must use a command")
         command, args = args[0], args[1:]
         if command not in qommon._commands:
             for cmd_prefix in self.cmd_prefixes:
@@ -112,5 +136,5 @@ class Ctl:
                     pass
         command_class = qommon._commands[command]
         cmd = command_class()
-        return cmd.run(args)
+        return cmd.run(args, options)
 
