diff --git a/bin/update-configuration-table.js b/bin/update-configuration-table.js
new file mode 100644
index 0000000000000000000000000000000000000000..d68521c0508296c2199879b4c6fc9b6076b9c5a3
--- /dev/null
+++ b/bin/update-configuration-table.js
@@ -0,0 +1,32 @@
+#!/usr/bin/env node
+const stringify = require('json-stringify-pretty-compact');
+
+const definitions = require('../lib/config/definitions');
+const defaultsParser = require('../lib/config/defaults');
+const cliParser = require('../lib/config/cli');
+const envParser = require('../lib/config/env');
+
+/* eslint-disable no-console */
+// Print table header
+console.log('## Configuration Options');
+console.log('');
+console.log('| Name | Description | Type | Default value | Environment | CLI |');
+console.log('|------|-------------|------|---------------|-------------|-----|');
+
+const options = definitions.getOptions();
+options.forEach((option) => {
+  let optionDefault = defaultsParser.getDefault(option);
+  if (optionDefault !== '') {
+    optionDefault = `\`${stringify(optionDefault)}\``;
+  }
+  let envName = envParser.getEnvName(option);
+  if (envName.length) {
+    envName = `\`${envName}\``;
+  }
+  let cliName = cliParser.getCliName(option);
+  if (cliName.length) {
+    cliName = `\`${cliName}\``;
+  }
+  console.log(`| \`${option.name}\` | ${option.description} | ${option.type} | ${optionDefault} | ${envName} | ${cliName} |`);
+});
+/* eslint-enable no-console */
diff --git a/bin/update-docs.sh b/bin/update-docs.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9cb874f878638cf4695b9049cea19fdd2b4fd036
--- /dev/null
+++ b/bin/update-docs.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+perl -0777 -i -pe 's/\n  Usage:.*package-test\n/`node renovate --help`/se' readme.md
+perl -0777 -i -pe 's/\n  Usage:.*package-test\n/`node renovate --help`/se' docs/configuration.md
+perl -0777 -i -pe 's/## Configuration Options.*//se' docs/configuration.md
+node bin/update-configuration-table.js >> docs/configuration.md
diff --git a/docs/configuration.md b/docs/configuration.md
index 2c42c39065917f9a2ebf56a892347408ce13d473..3dc8fe9d21a029dd107f541d0e6f8d3296ca8863 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -56,24 +56,25 @@ $ node renovate --help
 
   Options:
 
-    -h, --help                               output usage information
-    -t, --token <token>                      GitHub Auth Token
-    -p, --package-files <list>               List of package.json file names
-    -d, --dep-types <list>                   List of dependency types
-    -i, --ignore-deps <list>                 List of dependencies to ignore
-    -b, --labels <list>                      List of labels to add
-    -b, --assignees <list>                   List of assignees to add
-    -r, --ignore-future [true/false]         Ignore versions tagged as "future"
-    -r, --ignore-unstable [true/false]       Ignore versions with unstable semver
-    -r, --respect-latest [true/false]        Ignore versions newer than dependency's "latest"
-    -r, --recreate-closed [true/false]       Recreate PR even if same was previously closed
-    -r, --recreate-unmergeable [true/false]  Recreate PR if existing branch is unmergeable
-    -l, --log-level <level>                  Log Level
+    -h, --help                        output usage information
+    --enabled [boolean]               Enable or disable renovate
+    --token <string>                  GitHub Auth Token
+    --package-files <list>            Package file paths
+    --dep-types <list>                Dependency types
+    --ignore-deps <list>              Dependencies to ignore
+    --ignore-future [boolean]         Ignore versions tagged as "future"
+    --ignore-unstable [boolean]       Ignore versions with unstable semver
+    --respect-latest [boolean]        Ignore versions newer than npm "latest" version
+    --recreate-closed [boolean]       Recreate PRs even if same ones were closed previously
+    --recreate-unmergeable [boolean]  Close and recreate PR if it has a merge conflict
+    --labels <list>                   Labels to add to Pull Request
+    --assignees <list>                Assignees for Pull Request
+    --log-level <string>              Logging level
 
   Examples:
 
     $ renovate --token abc123 singapore/lint-condo
-    $ renovate --ignore-unstable=false -l verbose singapore/lint-condo
+    $ renovate --ignore-unstable=false --log-level verbose singapore/lint-condo
     $ renovate singapore/lint-condo singapore/package-test
 ```
 
@@ -98,19 +99,23 @@ Obviously, you can't set repository or package file location with this method.
 
 ## Configuration Options
 
-| Option | Description | Default value | File | Environment | CLI |
-|---------------------|---------------------------------------------------------|-----------------------------------------------------------|---------------------------|--------------------------|---------------------------|
-| Token | GitHub Personal Access Token |  | `token` | `GITHUB_TOKEN` | `--token` |
-| Enabled | Enable/Disable renovate for this repo or file | true | `enabled` | | |
-| Repositories | List of Repositories |  | `repositories` | `RENOVATE_REPOS` | Space-delimited arguments |
-| Package Files | Package file location(s) | `[]` | `repository.packageFiles` | `RENOVATE_PACKAGE_FILES` | `--package-files` |
-| Dependency Types | Sections of package.json to renovate | `dependencies`, `devDependencies`, `optionalDependencies` | `depTypes` | `RENOVATE_DEP_TYPES` | `--dep-types` |
-| Ignore Dependencies | Dependencies to be ignored |  | `ignoreDeps` | `RENOVATE_IGNORE_DEPS` | `--ignore-deps` |
-| Labels | Labels to add to Pull Requests |  | `labels` | `RENOVATE_LABELS` | `--labels` |
-| Ignore Future | Ignore versions tagged as "future" | `true` | `ignoreFuture` | `RENOVATE_IGNORE_FUTURE` | `--ignore-future` |
-| Ignore Unstable | Ignore versions with unstable semver | `true` | `ignoreUnstable` | `RENOVATE_IGNORE_UNSTABLE` | `--ignore-unstable` |
-| Respect latest | Respect the "latest" tag in npm and don't upgrade past it | `true` | `respectLatest` | `RENOVATE_RESPECT_LATEST` | `--respect-latest` |
-| Recreate Closed | Create New PR even if same one was previously closed | `false` | `recreateClosed` | `RENOVATE_RECREATE_CLOSED` | `--recreate-closed` |
-| Recreate Unmergeable | Close and recreate PR if existing one is unmergeable | `true` | `recreateUnmergeable` | `RENOVATE_RECREATE_UNMERGEABLE` | `--recreate-unmergeable` |
-| Log Level | Log Level | `info` | `logLevel` | `LOG_LEVEL` | `--log-level` |
-| Templates | Handlebars templates for commit, branch and PR | Multiple | `templates` |  |  |
+| Name | Description | Type | Default value | Environment | CLI |
+|------|-------------|------|---------------|-------------|-----|
+| `enabled` | Enable or disable renovate | boolean | `true` | `RENOVATE_ENABLED` | `--enabled` |
+| `token` | GitHub Auth Token | string | `null` | `GITHUB_TOKEN` | `--token` |
+| `repositories` | GitHub repositories | list | `[]` | `RENOVATE_REPOSITORIES` |  |
+| `packageFiles` | Package file paths | list | `[]` | `RENOVATE_PACKAGE_FILES` | `--package-files` |
+| `depTypes` | Dependency types | list | `["dependencies", "devDependencies", "optionalDependencies"]` | `RENOVATE_DEP_TYPES` | `--dep-types` |
+| `ignoreDeps` | Dependencies to ignore | list | `[]` | `RENOVATE_IGNORE_DEPS` | `--ignore-deps` |
+| `ignoreFuture` | Ignore versions tagged as "future" | boolean | `true` | `RENOVATE_IGNORE_FUTURE` | `--ignore-future` |
+| `ignoreUnstable` | Ignore versions with unstable semver | boolean | `true` | `RENOVATE_IGNORE_UNSTABLE` | `--ignore-unstable` |
+| `respectLatest` | Ignore versions newer than npm "latest" version | boolean | `true` | `RENOVATE_RESPECT_LATEST` | `--respect-latest` |
+| `recreateClosed` | Recreate PRs even if same ones were closed previously | boolean | `false` | `RENOVATE_RECREATE_CLOSED` | `--recreate-closed` |
+| `recreateUnmergeable` | Close and recreate PR if it has a merge conflict | boolean | `true` | `RENOVATE_RECREATE_UNMERGEABLE` | `--recreate-unmergeable` |
+| `branchName` | Branch name template | string | `"renovate/{{depName}}-{{newVersionMajor}}.x"` |  |  |
+| `commitMessage` | Commit message template | string | `"Update dependency {{depName}} to version {{newVersion}}"` |  |  |
+| `prTitle` | Pull Request title template | string | `"{{#if isPin}}Pin{{else}}Update{{/if}} dependency {{depName}} to version {{#if isMajor}}{{newVersionMajor}}.x{{else}}{{newVersion}}{{/if}}"` |  |  |
+| `prBody` | Pull Request body template | string | `"This Pull Request updates dependency {{depName}} from version {{currentVersion}} to {{newVersion}}\n\n{{changelog}}"` |  |  |
+| `labels` | Labels to add to Pull Request | list | `[]` | `RENOVATE_LABELS` | `--labels` |
+| `assignees` | Assignees for Pull Request | list | `[]` | `RENOVATE_ASSIGNEES` | `--assignees` |
+| `logLevel` | Logging level | string | `"info"` | `LOG_LEVEL` | `--log-level` |
diff --git a/lib/config/cli.js b/lib/config/cli.js
index 4f2e79f16aea5088842c40956172843d3a8c66f9..d37066c3858eb872485aabb239b3bbad74bb4f46 100644
--- a/lib/config/cli.js
+++ b/lib/config/cli.js
@@ -1,86 +1,68 @@
-const logger = require('winston');
-const program = require('commander');
+const clearRequire = require('clear-require');
+let commander = require('commander');
+const configDefinitions = require('./definitions');
 
-const config = {};
+module.exports = {
+  getCliName,
+  getConfig,
+};
 
-program
-  .arguments('[repositories...]')
-  .option('-t, --token <token>', 'GitHub Auth Token')
-  .option('-p, --package-files <list>', 'List of package.json file names', list)
-  .option('-d, --dep-types <list>', 'List of dependency types', list)
-  .option('-i, --ignore-deps <list>', 'List of dependencies to ignore', list)
-  .option('-b, --labels <list>', 'List of labels to add', list)
-  .option('-b, --assignees <list>', 'List of assignees to add', list)
-  .option('-r, --ignore-future [true/false]', 'Ignore versions tagged as "future"', bool)
-  .option('-r, --ignore-unstable [true/false]', 'Ignore versions with unstable semver')
-  .option('-r, --respect-latest [true/false]', 'Ignore versions newer than dependency\'s "latest"')
-  .option('-r, --recreate-closed [true/false]', 'Recreate PR even if same was previously closed')
-  .option('-r, --recreate-unmergeable [true/false]', 'Recreate PR if existing branch is unmergeable')
-  .option('-l, --log-level <level>', 'Log Level')
-  .on('--help', () => {
+function getCliName(option) {
+  if (option.cli === false) {
+    return '';
+  }
+  const nameWithHyphens = option.name.replace(/([A-Z])/g, '-$1');
+  return `--${nameWithHyphens.toLowerCase()}`;
+}
+
+function getConfig(argv) {
+  clearRequire('commander');
+  commander = require('commander'); // eslint-disable-line
+  const options = configDefinitions.getOptions();
+
+  const config = {};
+
+  const coersions = {
+    boolean: val => (val === 'true'),
+    list: val => val.split(',').map(el => el.trim()),
+    string: val => val,
+  };
+
+  let program = commander.arguments('[repositories...]');
+
+  options.forEach((option) => {
+    if (option.cli !== false) {
+      const param = `<${option.type}>`.replace('<boolean>', '[boolean]');
+      const optionString = `${getCliName(option)} ${param}`;
+      program = program.option(optionString, option.description, coersions[option.type]);
+    }
+  });
+
+  /* istanbul ignore next */
+  function helpConsole() {
     /* eslint-disable no-console */
     console.log('  Examples:');
     console.log('');
     console.log('    $ renovate --token abc123 singapore/lint-condo');
-    console.log('    $ renovate --ignore-unstable=false -l verbose singapore/lint-condo');
+    console.log('    $ renovate --ignore-unstable=false --log-level verbose singapore/lint-condo');
     console.log('    $ renovate singapore/lint-condo singapore/package-test');
-    console.log('');
     /* eslint-enable no-console */
-  })
-  .action((repositories) => {
-    config.repositories = repositories;
-  })
-  .parse(process.argv);
-
-if (program.depTypes) {
-  config.depTypes = program.depTypes;
-}
-if (program.ignoreDeps) {
-  config.ignoreDeps = program.ignoreDeps;
-}
-if (program.labels) {
-  config.labels = program.labels;
-}
-if (program.assignees) {
-  config.assignees = program.assignees;
-}
-if (program.logLevel) {
-  config.logLevel = program.logLevel;
-}
-if (program.packageFiles) {
-  config.packageFiles = program.packageFiles;
-}
-if (program.ignoreFuture) {
-  config.ignoreFuture = program.ignoreFuture;
-}
-if (program.ignoreUnstable) {
-  config.ignoreUnstable = program.ignoreUnstable;
-}
-if (program.respectLatest) {
-  config.respectLatest = program.respectLatest;
-}
-if (program.recreateClosed) {
-  config.recreateClosed = program.recreateClosed;
-}
-if (program.recreateUnmergeable) {
-  config.recreateUnmergeable = program.recreateUnmergeable;
-}
-if (program.token) {
-  config.token = program.token;
-}
+  }
 
-module.exports = config;
+  program = program
+    .on('--help', helpConsole)
+    .action((repositories) => {
+      config.repositories = repositories;
+    })
+    .parse(argv);
 
-function list(val) {
-  return val.split(',');
-}
+  options.forEach((option) => {
+    if (option.cli !== false) {
+      if (program[option.name] !== undefined) {
+        config[option.name] = program[option.name];
+      }
+    }
+  });
 
-function bool(val) {
-  if (val === 'true') {
-    return true;
-  } else if (val === 'false') {
-    return false;
-  }
-  logger.error(`Boolean option must be true or false (is: "${val}")`);
-  return process.exit(1);
+  return config;
 }
diff --git a/lib/config/default.js b/lib/config/default.js
deleted file mode 100644
index 028fed68157d78a519f616e0de8b90a293754b77..0000000000000000000000000000000000000000
--- a/lib/config/default.js
+++ /dev/null
@@ -1,18 +0,0 @@
-module.exports = {
-  enabled: true,
-  packageFiles: [], // Autodiscover
-  depTypes: ['dependencies', 'devDependencies', 'optionalDependencies'],
-  ignoreDeps: [],
-  assignees: [],
-  labels: [],
-  branchName: 'renovate/{{depName}}-{{newVersionMajor}}.x',
-  commitMessage: 'Update dependency {{depName}} to version {{newVersion}}',
-  prTitle: '{{#if isPin}}Pin{{else}}Update{{/if}} dependency {{depName}} to version {{#if isMajor}}{{newVersionMajor}}.x{{else}}{{newVersion}}{{/if}}',
-  prBody: 'This Pull Request updates dependency {{depName}} from version {{currentVersion}} to {{newVersion}}\n\n{{changelog}}',
-  ignoreFuture: true,
-  ignoreUnstable: true,
-  respectLatest: true,
-  recreateClosed: false,
-  recreateUnmergeable: true,
-  logLevel: 'info',
-};
diff --git a/lib/config/defaults.js b/lib/config/defaults.js
new file mode 100644
index 0000000000000000000000000000000000000000..103213c7a7a64028a228732c7a8a34c906831db5
--- /dev/null
+++ b/lib/config/defaults.js
@@ -0,0 +1,27 @@
+const configDefinitions = require('./definitions');
+
+module.exports = {
+  getDefault,
+  getConfig,
+};
+
+const defaultValues = {
+  boolean: true,
+  list: [],
+  string: null,
+};
+
+function getDefault(option) {
+  return option.default === undefined ? defaultValues[option.type] : option.default;
+}
+
+function getConfig() {
+  const options = configDefinitions.getOptions();
+  const config = {};
+
+  options.forEach((option) => {
+    config[option.name] = getDefault(option);
+  });
+
+  return config;
+}
diff --git a/lib/config/definitions.js b/lib/config/definitions.js
new file mode 100644
index 0000000000000000000000000000000000000000..78ec52db3841b6f013dae4c4cbce85d4e2a5ebfa
--- /dev/null
+++ b/lib/config/definitions.js
@@ -0,0 +1,125 @@
+// const logger = require('winston');
+
+module.exports = {
+  getOptions,
+};
+
+const options = [
+  {
+    name: 'enabled',
+    description: 'Enable or disable renovate',
+    type: 'boolean',
+  },
+  {
+    name: 'token',
+    description: 'GitHub Auth Token',
+    type: 'string',
+    env: 'GITHUB_TOKEN',
+  },
+  {
+    name: 'repositories',
+    description: 'GitHub repositories',
+    type: 'list',
+    cli: false,
+  },
+  {
+    name: 'packageFiles',
+    description: 'Package file paths',
+    type: 'list',
+  },
+  {
+    name: 'depTypes',
+    description: 'Dependency types',
+    type: 'list',
+    default: ['dependencies', 'devDependencies', 'optionalDependencies'],
+  },
+  // Version behaviour
+  {
+    name: 'ignoreDeps',
+    description: 'Dependencies to ignore',
+    type: 'list',
+  },
+  {
+    name: 'ignoreFuture',
+    description: 'Ignore versions tagged as "future"',
+    type: 'boolean',
+  },
+  {
+    name: 'ignoreUnstable',
+    description: 'Ignore versions with unstable semver',
+    type: 'boolean',
+  },
+  {
+    name: 'respectLatest',
+    description: 'Ignore versions newer than npm "latest" version',
+    type: 'boolean',
+  },
+  // PR Behaviour
+  {
+    name: 'recreateClosed',
+    description: 'Recreate PRs even if same ones were closed previously',
+    type: 'boolean',
+    default: false,
+  },
+  {
+    name: 'recreateUnmergeable',
+    description: 'Close and recreate PR if it has a merge conflict',
+    type: 'boolean',
+  },
+  // String templates
+  {
+    name: 'branchName',
+    description: 'Branch name template',
+    type: 'string',
+    default: 'renovate/{{depName}}-{{newVersionMajor}}.x',
+    cli: false,
+    env: false,
+  },
+  {
+    name: 'commitMessage',
+    description: 'Commit message template',
+    type: 'string',
+    default: 'Update dependency {{depName}} to version {{newVersion}}',
+    cli: false,
+    env: false,
+  },
+  {
+    name: 'prTitle',
+    description: 'Pull Request title template',
+    type: 'string',
+    default: '{{#if isPin}}Pin{{else}}Update{{/if}} dependency {{depName}} to version {{#if isMajor}}{{newVersionMajor}}.x{{else}}{{newVersion}}{{/if}}',
+    cli: false,
+    env: false,
+  },
+  {
+    name: 'prBody',
+    description: 'Pull Request body template',
+    type: 'string',
+    default: 'This Pull Request updates dependency {{depName}} from version {{currentVersion}} to {{newVersion}}\n\n{{changelog}}',
+    cli: false,
+    env: false,
+  },
+  // Pull Request options
+  {
+    name: 'labels',
+    description: 'Labels to add to Pull Request',
+    type: 'list',
+  },
+  {
+    name: 'assignees',
+    description: 'Assignees for Pull Request',
+    type: 'list',
+  },
+  // Debug options
+  {
+    name: 'logLevel',
+    description: 'Logging level',
+    type: 'string',
+    default: 'info',
+    env: 'LOG_LEVEL',
+  },
+];
+
+function getOptions() {
+  return options;
+}
diff --git a/lib/config/env.js b/lib/config/env.js
index 4e06e3f2870d5813d7fcd19b5d8634a4c9f81663..133e96fe83b75c03c0c0977ebf95b4b30a0426ac 100644
--- a/lib/config/env.js
+++ b/lib/config/env.js
@@ -1,59 +1,41 @@
-const logger = require('winston');
+const configDefinitions = require('./definitions');
 
-const config = {};
+module.exports = {
+  getEnvName,
+  getConfig,
+};
 
-if (process.env.GITHUB_TOKEN) {
-  config.token = process.env.GITHUB_TOKEN;
-}
-if (process.env.RENOVATE_REPOS) {
-  config.repositories = list(process.env.RENOVATE_REPOS);
-}
-if (process.env.RENOVATE_PACKAGE_FILES) {
-  config.packageFiles = list(process.env.PACKAGE_FILES);
-}
-if (process.env.RENOVATE_DEP_TYPES) {
-  config.depTypes = list(process.env.RENOVATE_DEP_TYPES);
-}
-if (process.env.RENOVATE_IGNORE_DEPS) {
-  config.ignoreDeps = list(process.env.RENOVATE_IGNORE_DEPS);
-}
-if (process.env.RENOVATE_LABELS) {
-  config.labels = list(process.env.RENOVATE_LABELS);
-}
-if (process.env.RENOVATE_ASSIGNEES) {
-  config.assignees = list(process.env.RENOVATE_ASSIGNEES);
-}
-if (process.env.RENOVATE_IGNORE_FUTURE) {
-  config.ignoreFuture = bool(process.env.RENOVATE_IGNORE_FUTURE);
-}
-if (process.env.RENOVATE_IGNORE_UNSTABLE) {
-  config.ignoreUnstable = bool(process.env.RENOVATE_IGNORE_UNSTABLE);
-}
-if (process.env.RENOVATE_RESPECT_LATEST) {
-  config.respectLatest = bool(process.env.RENOVATE_RESPECT_LATEST);
-}
-if (process.env.RENOVATE_RECREATE_CLOSED) {
-  config.recreateClosed = bool(process.env.RENOVATE_RECREATE_CLOSED);
-}
-if (process.env.RENOVATE_RECREATE_UNMERGEABLE) {
-  config.recreateUnmergeable = bool(process.env.RENOVATE_RECREATE_UNMERGEABLE);
-}
-if (process.env.LOG_LEVEL) {
-  config.logLevel = process.env.LOG_LEVEL;
+function getEnvName(option) {
+  if (option.env === false) {
+    return '';
+  }
+  if (option.env) {
+    return option.env;
+  }
+  const nameWithUnderscores = option.name.replace(/([A-Z])/g, '_$1');
+  return `RENOVATE_${nameWithUnderscores.toUpperCase()}`;
 }
 
-module.exports = config;
+function getConfig(env) {
+  const options = configDefinitions.getOptions();
 
-function list(val) {
-  return val.split(',').map(el => el.trim());
-}
+  const config = {};
 
-function bool(val) {
-  if (val === 'true') {
-    return true;
-  } else if (val === 'false') {
-    return false;
-  }
-  logger.error(`Boolean environment variable must be true or false (is: "${val}")`);
-  return process.exit(1);
+  const coersions = {
+    boolean: val => (val === 'true'),
+    list: val => val.split(',').map(el => el.trim()),
+    string: val => val,
+  };
+
+  options.forEach((option) => {
+    if (option.env !== false) {
+      const envName = getEnvName(option);
+      if (env[envName]) {
+        const coerce = coersions[option.type];
+        config[option.name] = coerce(env[envName]);
+      }
+    }
+  });
+
+  return config;
 }
diff --git a/lib/config/file.js b/lib/config/file.js
index 44103039e1873631d240a0cafd444706eaa758ca..b103f6a9de720a7e9ba28e1130829c7da0e64288 100644
--- a/lib/config/file.js
+++ b/lib/config/file.js
@@ -1,21 +1,26 @@
 const logger = require('winston');
 
-let configFile = process.env.RENOVATE_CONFIG_FILE || 'config';
-if (!isPathAbsolute(configFile)) {
-  configFile = `../../${configFile}`;
-}
+module.exports = {
+  getConfig,
+  isPathAbsolute,
+};
 
-let config = {};
-try {
-  // eslint-disable-next-line global-require,import/no-dynamic-require
-  config = require(configFile);
-} catch (err) {
-  // Do nothing
-  logger.verbose('Could not locate config file');
+function getConfig(env) {
+  let configFile = env.RENOVATE_CONFIG_FILE || 'config';
+  if (!isPathAbsolute(configFile)) {
+    configFile = `../../${configFile}`;
+  }
+  let config = {};
+  try {
+    // eslint-disable-next-line global-require,import/no-dynamic-require
+    config = require(configFile);
+  } catch (err) {
+    // Do nothing
+    logger.verbose('Could not locate config file');
+  }
+  return config;
 }
 
-module.exports = config;
-
 function isPathAbsolute(path) {
   return /^(?:\/|[a-z]+:\/\/)/.test(path);
 }
diff --git a/lib/config/index.js b/lib/config/index.js
index e24189da4c95c9e15018a03bafa88831866bbc59..d39656391f290177088c4855c6d50578b09b978f 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -1,19 +1,27 @@
 const logger = require('winston');
-const program = require('commander');
 const stringify = require('json-stringify-pretty-compact');
 
+const defaultsParser = require('./defaults');
+const fileParser = require('./file');
+const cliParser = require('./cli');
+const envParser = require('./env');
+
 let config = null;
 
-function parseConfigs() {
+module.exports = {
+  parseConfigs,
+  getCascadedConfig,
+  getGlobalConfig,
+};
+
+function parseConfigs(env, argv) {
   logger.debug('Parsing configs');
 
   // Get configs
-  /* eslint-disable global-require */
-  const defaultConfig = require('./default');
-  const fileConfig = require('./file');
-  const cliConfig = require('./cli');
-  const envConfig = require('./env');
-  /* eslint-enable global-require */
+  const defaultConfig = defaultsParser.getConfig();
+  const fileConfig = fileParser.getConfig(env);
+  const cliConfig = cliParser.getConfig(argv);
+  const envConfig = envParser.getConfig(env);
 
   logger.debug(`Default config = ${redact(defaultConfig)}`);
   logger.debug(`File config = ${redact(fileConfig)}`);
@@ -27,20 +35,13 @@ function parseConfigs() {
   // Set log level
   logger.level = config.logLevel;
 
-  // Save default templates
-  config.defaultTemplates = defaultConfig.templates;
-
   // Check for token
-  if (typeof config.token === 'undefined') {
-    logger.error('A GitHub token must be configured');
-    program.outputHelp();
-    process.exit(1);
+  if (config.token === null) {
+    throw new Error('A GitHub token must be configured');
   }
   // We need at least one repository defined
   if (!config.repositories || config.repositories.length === 0) {
-    logger.error('At least one repository must be configured');
-    program.outputHelp();
-    process.exit(1);
+    throw new Error('At least one repository must be configured');
   }
   // Convert any repository strings to objects
   config.repositories.forEach((repo, index) => {
@@ -69,8 +70,6 @@ function parseConfigs() {
 
 function getCascadedConfig(repo, packageFile) {
   const cascadedConfig = Object.assign({}, config, repo, packageFile);
-  // Fill in any missing templates with defaults
-  cascadedConfig.templates = Object.assign({}, config.defaultTemplates, cascadedConfig.templates);
   // Remove unnecessary fields
   delete cascadedConfig.repositories;
   delete cascadedConfig.repository;
@@ -90,9 +89,3 @@ function redact(inputConfig) {
   const redactedConfig = Object.assign({}, inputConfig, tokenConfig);
   return stringify(redactedConfig);
 }
-
-module.exports = {
-  getCascadedConfig,
-  getGlobalConfig,
-  parseConfigs,
-};
diff --git a/lib/index.js b/lib/index.js
index 003968ed162ec7af9aedce6e8cbc6ac86c828f8a..fffc1d4451cfda87342acafac8e2539086b946a8 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -12,7 +12,12 @@ module.exports = {
 
 function start() {
   // Parse config
-  config.parseConfigs();
+  try {
+    config.parseConfigs(process.env, process.argv);
+  } catch (error) {
+    logger.error(error.message);
+    process.exit(-1);
+  }
 
   // Initialize our promise chain
   let p = Promise.resolve();
diff --git a/package.json b/package.json
index b63f0b762dafe857023d392265a51ed29c801af7..10ae010243871c75e0af876e0a6635034fc16d1b 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,10 @@
     "heroku-scheduler": "heroku addons:open scheduler",
     "istanbul": "istanbul cover _mocha test",
     "mocha": "mocha test",
+    "publish": "npm run update-docs && np",
     "renovate": "node renovate",
-    "test": "npm run eslint && npm run istanbul"
+    "test": "npm run eslint && npm run istanbul",
+    "update-docs": "bash bin/update-docs.sh"
   },
   "repository": {
     "type": "git",
@@ -34,6 +36,7 @@
   },
   "dependencies": {
     "changelog": "dylang/changelog#v1.2.0",
+    "clear-require": "1.0.1",
     "commander": "2.9.0",
     "gh-got": "5.0.0",
     "got": "6.7.1",
@@ -51,6 +54,7 @@
     "eslint-plugin-import": "2.2.0",
     "eslint-plugin-promise": "3.4.0",
     "istanbul": "0.4.5",
-    "mocha": "3.2.0"
+    "mocha": "3.2.0",
+    "np": "2.12.0"
   }
 }
diff --git a/readme.md b/readme.md
index 62b108b6b700cdf38f5fadd3f1b8ddd022362b4d..01b1cca561b0a9ddbc7bb658891e7a7d41f483fc 100644
--- a/readme.md
+++ b/readme.md
@@ -27,7 +27,7 @@ The script will need a GitHub Personal Access Token with "repo" permissions. You
 This token needs to be configured via file, environment variable, or CLI. See [docs/configuration.md](docs/configuration.md) for details.
 The simplest way is to expose it as `GITHUB_TOKEN`.
 
-## Usage (CLI)
+## Usage
 
 ```
 $ node renovate --help
@@ -36,24 +36,25 @@ $ node renovate --help
 
   Options:
 
-    -h, --help                               output usage information
-    -t, --token <token>                      GitHub Auth Token
-    -p, --package-files <list>               List of package.json file names
-    -d, --dep-types <list>                   List of dependency types
-    -i, --ignore-deps <list>                 List of dependencies to ignore
-    -b, --labels <list>                      List of labels to add
-    -b, --assignees <list>                   List of assignees to add
-    -r, --ignore-future [true/false]         Ignore versions tagged as "future"
-    -r, --ignore-unstable [true/false]       Ignore versions with unstable semver
-    -r, --respect-latest [true/false]        Ignore versions newer than dependency's "latest"
-    -r, --recreate-closed [true/false]       Recreate PR even if same was previously closed
-    -r, --recreate-unmergeable [true/false]  Recreate PR if existing branch is unmergeable
-    -l, --log-level <level>                  Log Level
+    -h, --help                        output usage information
+    --enabled [boolean]               Enable or disable renovate
+    --token <string>                  GitHub Auth Token
+    --package-files <list>            Package file paths
+    --dep-types <list>                Dependency types
+    --ignore-deps <list>              Dependencies to ignore
+    --ignore-future [boolean]         Ignore versions tagged as "future"
+    --ignore-unstable [boolean]       Ignore versions with unstable semver
+    --respect-latest [boolean]        Ignore versions newer than npm "latest" version
+    --recreate-closed [boolean]       Recreate PRs even if same ones were closed previously
+    --recreate-unmergeable [boolean]  Close and recreate PR if it has a merge conflict
+    --labels <list>                   Labels to add to Pull Request
+    --assignees <list>                Assignees for Pull Request
+    --log-level <string>              Logging level
 
   Examples:
 
     $ renovate --token abc123 singapore/lint-condo
-    $ renovate --ignore-unstable=false -l verbose singapore/lint-condo
+    $ renovate --ignore-unstable=false --log-level verbose singapore/lint-condo
     $ renovate singapore/lint-condo singapore/package-test
 ```
 
diff --git a/test/.eslintrc.js b/test/.eslintrc.js
index 9ec8ec428d248ad9015babbfed097523094b90e6..1deb8028f830049b2c8994a9cfeca2360d42d983 100644
--- a/test/.eslintrc.js
+++ b/test/.eslintrc.js
@@ -2,4 +2,7 @@ module.exports = {
     'env': {
       'mocha': true,
     },
+    'rules': {
+      'import/no-extraneous-dependencies': 0,
+    },
 };
diff --git a/test/_fixtures/config/argv.js b/test/_fixtures/config/argv.js
new file mode 100644
index 0000000000000000000000000000000000000000..e4944d56168fb16c35881294bc4bc5bd36786468
--- /dev/null
+++ b/test/_fixtures/config/argv.js
@@ -0,0 +1,4 @@
+module.exports = [
+  '/usr/local/bin/node',
+  '/Users/me/github/renovate/renovate',
+];
diff --git a/test/_fixtures/config/file.js b/test/_fixtures/config/file.js
new file mode 100644
index 0000000000000000000000000000000000000000..54eda96d35fbca389300b0b85d6fda2622c7b51a
--- /dev/null
+++ b/test/_fixtures/config/file.js
@@ -0,0 +1,20 @@
+module.exports = {
+  token: 'abcdefg',
+  logLevel: 'verbose',
+  repositories: [
+    'singapore/lint-condo',
+    {
+      repository: 'singapore/renovate',
+      packageFiles: ['package2.json'],
+    },
+    {
+      repository: 'singapore/renovate',
+      packageFiles: [
+        {
+          fileName: 'package.json',
+          labels: ['a'],
+        },
+      ],
+    },
+  ],
+};
diff --git a/test/chai.js b/test/chai.js
new file mode 100644
index 0000000000000000000000000000000000000000..41e09fc5389f881e86ec4f8b9711cb43f255d111
--- /dev/null
+++ b/test/chai.js
@@ -0,0 +1 @@
+require('chai').should();
diff --git a/test/config/cli.js b/test/config/cli.js
new file mode 100644
index 0000000000000000000000000000000000000000..f583d1f118a57aa66b4077ad4f47932f8b3f376d
--- /dev/null
+++ b/test/config/cli.js
@@ -0,0 +1,66 @@
+const cli = require('../../lib/config/cli.js');
+let argv = require('../_fixtures/config/argv');
+
+describe('config/cli', () => {
+  describe('.getCliName(definition)', () => {
+    it('generates CLI value', () => {
+      const option = {
+        name: 'oneTwoThree',
+      };
+      cli.getCliName(option).should.eql('--one-two-three');
+    });
+  });
+  describe('.getConfig(argv)', () => {
+    it('returns empty argv', () => {
+      cli.getConfig(argv).should.eql({});
+    });
+    it('supports boolean no value', () => {
+      argv.push('--recreate-closed');
+      cli.getConfig(argv).should.eql({ recreateClosed: true });
+      argv = argv.slice(0, -1);
+    });
+    it('supports boolean space true', () => {
+      argv.push('--recreate-closed');
+      argv.push('true');
+      cli.getConfig(argv).should.eql({ recreateClosed: true });
+      argv = argv.slice(0, -2);
+    });
+    it('supports boolean space false', () => {
+      argv.push('--recreate-closed');
+      argv.push('false');
+      cli.getConfig(argv).should.eql({ recreateClosed: false });
+      argv = argv.slice(0, -2);
+    });
+    it('supports boolean equals true', () => {
+      argv.push('--recreate-closed=true');
+      cli.getConfig(argv).should.eql({ recreateClosed: true });
+      argv = argv.slice(0, -1);
+    });
+    it('supports boolean equals false', () => {
+      argv.push('--recreate-closed=false');
+      cli.getConfig(argv).should.eql({ recreateClosed: false });
+      argv = argv.slice(0, -1);
+    });
+    it('supports list single', () => {
+      argv.push('--labels=a');
+      cli.getConfig(argv).should.eql({ labels: ['a'] });
+      argv = argv.slice(0, -1);
+    });
+    it('supports list multiple', () => {
+      argv.push('--labels=a,b,c');
+      cli.getConfig(argv).should.eql({ labels: ['a', 'b', 'c'] });
+      argv = argv.slice(0, -1);
+    });
+    it('supports string', () => {
+      argv.push('--token=a');
+      cli.getConfig(argv).should.eql({ token: 'a' });
+      argv = argv.slice(0, -1);
+    });
+    it('supports repositories', () => {
+      argv.push('foo');
+      argv.push('bar');
+      cli.getConfig(argv).should.eql({ repositories: ['foo', 'bar'] });
+      argv = argv.slice(0, -2);
+    });
+  });
+});
diff --git a/test/config/env.js b/test/config/env.js
new file mode 100644
index 0000000000000000000000000000000000000000..6e7f1b6a6189ad59308f7fb235e29b3e3417202a
--- /dev/null
+++ b/test/config/env.js
@@ -0,0 +1,49 @@
+const env = require('../../lib/config/env.js');
+
+describe('config/env', () => {
+  describe('.getConfig(env)', () => {
+    it('returns empty env', () => {
+      env.getConfig({}).should.eql({});
+    });
+    it('supports boolean true', () => {
+      const envParam = { RENOVATE_RECREATE_CLOSED: 'true' };
+      env.getConfig(envParam).should.eql({ recreateClosed: true });
+    });
+    it('supports boolean false', () => {
+      const envParam = { RENOVATE_RECREATE_CLOSED: 'false' };
+      env.getConfig(envParam).should.eql({ recreateClosed: false });
+    });
+    it('supports boolean nonsense as false', () => {
+      const envParam = { RENOVATE_RECREATE_CLOSED: 'foo' };
+      env.getConfig(envParam).should.eql({ recreateClosed: false });
+    });
+    delete process.env.RENOVATE_RECREATE_CLOSED;
+    it('supports list single', () => {
+      const envParam = { RENOVATE_LABELS: 'a' };
+      env.getConfig(envParam).should.eql({ labels: ['a'] });
+    });
+    it('supports list multiple', () => {
+      const envParam = { RENOVATE_LABELS: 'a,b,c' };
+      env.getConfig(envParam).should.eql({ labels: ['a', 'b', 'c'] });
+    });
+    it('supports string', () => {
+      const envParam = { GITHUB_TOKEN: 'a' };
+      env.getConfig(envParam).should.eql({ token: 'a' });
+    });
+  });
+  describe('.getEnvName(definition)', () => {
+    it('returns existing env', () => {
+      const option = {
+        name: 'foo',
+        env: 'FOO',
+      };
+      env.getEnvName(option).should.eql('FOO');
+    });
+    it('generates RENOVATE_ env', () => {
+      const option = {
+        name: 'oneTwoThree',
+      };
+      env.getEnvName(option).should.eql('RENOVATE_ONE_TWO_THREE');
+    });
+  });
+});
diff --git a/test/config/file.js b/test/config/file.js
new file mode 100644
index 0000000000000000000000000000000000000000..a6f03fb9cdefec829180a3a4e51e44dfcfa5a2dc
--- /dev/null
+++ b/test/config/file.js
@@ -0,0 +1,13 @@
+const file = require('../../lib/config/file.js');
+const customConfig = require('../_fixtures/config/file');
+
+describe('config/file', () => {
+  describe('.getConfig()', () => {
+    it('returns empty env', () => {
+      file.getConfig({}).should.eql({});
+    });
+    it('parses custom config file', () => {
+      file.getConfig({ RENOVATE_CONFIG_FILE: 'test/_fixtures/config/file.js' }).should.eql(customConfig);
+    });
+  });
+});
diff --git a/test/config/index.js b/test/config/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..fb01569a9486b380c9f3e68ad659b9559530d2c3
--- /dev/null
+++ b/test/config/index.js
@@ -0,0 +1,40 @@
+const configParser = require('../../lib/config/index.js');
+const defaultArgv = require('../_fixtures/config/argv');
+const should = require('chai').should();
+
+describe('config/index', () => {
+  describe('.parseConfigs(env, defaultArgv)', () => {
+    it('throws for no token', () => {
+      const env = {};
+      configParser.parseConfigs.bind(configParser, env, defaultArgv).should.throw('A GitHub token must be configured');
+    });
+    it('supports token in env', () => {
+      const env = { GITHUB_TOKEN: 'abc' };
+      configParser.parseConfigs.bind(configParser, env, defaultArgv).should.throw('At least one repository must be configured');
+    });
+    it('supports token in CLI options', () => {
+      const env = {};
+      const argv = defaultArgv.concat(['--token=abc']);
+      configParser.parseConfigs.bind(configParser, env, argv).should.throw('At least one repository must be configured');
+    });
+    it('supports repositories in CLI', () => {
+      const env = {};
+      const argv = defaultArgv.concat(['--token=abc', 'foo']);
+      configParser.parseConfigs(env, argv);
+      const config = configParser.getGlobalConfig();
+      should.exist(config.token);
+      should.exist(config.repositories);
+      should.exist(config.recreateClosed);
+    });
+    it('gets cascaded config', () => {
+      const env = { RENOVATE_CONFIG_FILE: 'test/_fixtures/config/file.js' };
+      configParser.parseConfigs(env, defaultArgv);
+      const config = configParser.getGlobalConfig();
+      const repo = config.repositories.pop();
+      should.exist(repo);
+      const cascadedConfig = configParser.getCascadedConfig(repo, null);
+      should.exist(cascadedConfig.token);
+      should.exist(cascadedConfig.recreateClosed);
+    });
+  });
+});
diff --git a/test/helpers/package-json.js b/test/helpers/package-json.js
index 651a7113f0aa6098efdb1f56e43bbcbc9cab5275..24e6ca6f335621aa8350c0029c4e1541668df7de 100644
--- a/test/helpers/package-json.js
+++ b/test/helpers/package-json.js
@@ -1,4 +1,3 @@
-const expect = require('chai').expect;
 const fs = require('fs');
 const packageJson = require('../../lib/helpers/package-json');
 
@@ -33,19 +32,19 @@ describe('helpers/package-json', () => {
       const outputContent = fs.readFileSync('./test/_fixtures/package.json/outputs/011.json', 'utf8');
       const testContent =
         packageJson.setNewValue(input01Content, 'dependencies', 'cheerio', '0.22.1');
-      expect(testContent).to.equal(outputContent);
+      testContent.should.equal(outputContent);
     });
     it('replaces only the first instance of a value', () => {
       const outputContent = fs.readFileSync('./test/_fixtures/package.json/outputs/012.json', 'utf8');
       const testContent =
         packageJson.setNewValue(input01Content, 'devDependencies', 'angular-touch', '1.6.1');
-      expect(testContent).to.equal(outputContent);
+      testContent.should.equal(outputContent);
     });
     it('replaces only the second instance of a value', () => {
       const outputContent = fs.readFileSync('./test/_fixtures/package.json/outputs/013.json', 'utf8');
       const testContent =
         packageJson.setNewValue(input01Content, 'devDependencies', 'angular-sanitize', '1.6.1');
-      expect(testContent).to.equal(outputContent);
+      testContent.should.equal(outputContent);
     });
   });
 });
diff --git a/test/helpers/versions.js b/test/helpers/versions.js
index 8c639cdb4a43e8b78b8c169d943fb755337a4afb..318c27fb0383f7acb5cb6c8e2b79020350ee3e18 100644
--- a/test/helpers/versions.js
+++ b/test/helpers/versions.js
@@ -1,10 +1,6 @@
-const chai = require('chai');
 const versionsHelper = require('../../lib/helpers/versions');
-
-chai.should();
-
 const qJson = require('../_fixtures/npm/01.json');
-const defaultConfig = require('../../lib/config/default');
+const defaultConfig = require('../../lib/config/defaults').getConfig();
 
 describe('helpers/versions', () => {
   describe('.determineUpgrades(dep, currentVersion, defaultConfig)', () => {