diff --git a/README.rst b/README.rst
index 06a22b0028c2eae6f587b98693e44f22e9e753b3..5998d133b0d64dfdb05df6f303ae1585d63b3a9a 100644
--- a/README.rst
+++ b/README.rst
@@ -25,6 +25,7 @@ Features
 - Bootstrap 3 and Font Awesome 4 with starter templates
 - Flask-SQLAlchemy with basic User model
 - Easy database migrations with Flask-Migrate
+- Configuration in environment variables, as per `The Twelve-Factor App <https://12factor.net/config>`_
 - Flask-WTForms with login and registration forms
 - Flask-Login for authentication
 - Flask-Bcrypt for password hashing
diff --git a/cookiecutter.json b/cookiecutter.json
index 0ae232c6ed8a7e4a638282f51b8129d8aa19c2bb..c4a689fac03e11f751b44447af949ed97e8de891 100644
--- a/cookiecutter.json
+++ b/cookiecutter.json
@@ -4,5 +4,6 @@
 	"github_username": "sloria",
 	"project_name": "My Flask App",
 	"app_name": "myflaskapp",
-	"project_short_description": "A flasky app."
+	"project_short_description": "A flasky app.",
+	"use_pipenv": ["no", "yes"]
 }
diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py
new file mode 100644
index 0000000000000000000000000000000000000000..339ecfa655d36107fdbfcb92d616eafaa8262f12
--- /dev/null
+++ b/hooks/post_gen_project.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Post gen hook to ensure that the generated project
+hase only one package managment, either pipenv or pip."""
+import os
+import shutil
+import sys
+
+
+def clean_extra_package_managment_files():
+    """Removes either requirements files and folderor the Pipfile."""
+    use_pipenv = '{{cookiecutter.use_pipenv}}'
+    to_delete = []
+
+    if use_pipenv == 'yes':
+        to_delete = to_delete + ['requirements.txt', 'requirements']
+    else:
+        to_delete.append('Pipfile')
+
+    try:
+        for file_or_dir in to_delete:
+            if os.path.isfile(file_or_dir):
+                os.remove(file_or_dir)
+            else:
+                shutil.rmtree(file_or_dir)
+        sys.exit(0)
+    except OSError as e:
+        sys.stdout.write(
+            'While attempting to remove file(s) an error occurred'
+        )
+        sys.stdout.write('Error: {}'.format(e))
+
+
+if __name__ == '__main__':
+    clean_extra_package_managment_files()
diff --git a/tasks.py b/tasks.py
index 7e412cec31e0aa87d6be37156fc4f29ca4e3c8be..9d8d5dfb52c92a8f0c4802abfed34f646fb45953 100644
--- a/tasks.py
+++ b/tasks.py
@@ -13,7 +13,6 @@ with open(os.path.join(HERE, 'cookiecutter.json'), 'r') as fp:
     COOKIECUTTER_SETTINGS = json.load(fp)
 # Match default value of app_name from cookiecutter.json
 COOKIE = os.path.join(HERE, COOKIECUTTER_SETTINGS['app_name'])
-AUTOAPP = os.path.join(COOKIE, 'autoapp.py')
 REQUIREMENTS = os.path.join(COOKIE, 'requirements', 'dev.txt')
 
 
@@ -42,7 +41,8 @@ def clean(ctx):
 
 
 def _run_flask_command(ctx, command):
-    ctx.run('FLASK_APP={0} flask {1}'.format(AUTOAPP, command), echo=True)
+    os.chdir(COOKIE)
+    ctx.run('flask {0}'.format(command), echo=True)
 
 
 @task(pre=[clean, build])
@@ -52,12 +52,14 @@ def test(ctx):
             echo=True)
     _run_npm_command(ctx, 'run lint')
     os.chdir(COOKIE)
+    shutil.copyfile(os.path.join(COOKIE, '.env.example'),
+                    os.path.join(COOKIE, '.env'))
     _run_flask_command(ctx, 'lint')
     _run_flask_command(ctx, 'test')
 
+
 @task
 def readme(ctx, browse=False):
-    ctx.run("rst2html.py README.rst > README.html")
+    ctx.run('rst2html.py README.rst > README.html')
     if browse:
         webbrowser.open_new_tab('README.html')
-
diff --git a/{{cookiecutter.app_name}}/.env.example b/{{cookiecutter.app_name}}/.env.example
new file mode 100644
index 0000000000000000000000000000000000000000..72367469b3b985bef77f520ffd4265033e820e5f
--- /dev/null
+++ b/{{cookiecutter.app_name}}/.env.example
@@ -0,0 +1,5 @@
+# Environment variable overrides for local development
+FLASK_APP=autoapp.py
+FLASK_ENV=development
+DATABASE_URL="sqlite:////tmp/dev.db"
+SECRET_KEY="not-so-secret"
diff --git a/{{cookiecutter.app_name}}/.gitignore b/{{cookiecutter.app_name}}/.gitignore
index be872525b75b9a0185aa7da3b41b5677d1ad07e4..24d2ea20b591eeb3960e56346b417c9ba781ffd4 100644
--- a/{{cookiecutter.app_name}}/.gitignore
+++ b/{{cookiecutter.app_name}}/.gitignore
@@ -51,3 +51,9 @@ env/
 # webpack-built files
 /{{cookiecutter.app_name}}/static/build/
 /{{cookiecutter.app_name}}/webpack/manifest.json
+
+# Configuration
+.env
+
+# Development database
+*.db
diff --git a/{{cookiecutter.app_name}}/Pipfile b/{{cookiecutter.app_name}}/Pipfile
new file mode 100644
index 0000000000000000000000000000000000000000..117ba62bb9f19e66c3a8054792ecf2da294d90b1
--- /dev/null
+++ b/{{cookiecutter.app_name}}/Pipfile
@@ -0,0 +1,60 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+# Flask
+Flask = "==1.0.2"
+MarkupSafe = "==1.0"
+Werkzeug = "==0.14.1"
+Jinja2 = "==2.10"
+itsdangerous = "==0.24"
+click = ">=5.0"
+
+# Database
+Flask-SQLAlchemy = "==2.3.2"
+psycopg2 = "==2.7.5"
+SQLAlchemy = "==1.2.8"
+
+# Migrations
+Flask-Migrate = "==2.2.0"
+
+# Forms
+Flask-WTF = "==0.14.2"
+WTForms = "==2.2.1"
+
+# Deployment
+gunicorn = ">=19.1.1"
+
+# Webpack
+flask-webpack = "==0.1.0"
+
+# Auth
+Flask-Login = "==0.4.1"
+Flask-Bcrypt = "==0.7.1"
+
+# Caching
+Flask-Caching = ">=1.0.0"
+
+# Debug toolbar
+Flask-DebugToolbar = "==0.10.1"
+
+# Environment variable parsing
+environs = "==4.0.0"
+
+[dev-packages]
+# Testing
+pytest = "==3.6.1"
+WebTest = "==2.0.30"
+factory-boy = "==2.11.*"
+
+# Lint and code style
+flake8 = "==3.5.0"
+flake8-blind-except = "==0.1.1"
+flake8-debugger = "==3.1.0"
+flake8-docstrings = "==1.3.0"
+flake8-isort = "==2.5"
+flake8-quotes = "==1.0.0"
+isort = "==4.3.4"
+pep8-naming = "==0.7.0"
diff --git a/{{cookiecutter.app_name}}/README.rst b/{{cookiecutter.app_name}}/README.rst
index 7b4d8d87a108837d6d40c14bb7fc50c05cfd921d..0a895306ed8cdd28f15db936ccac42816b7bd420 100644
--- a/{{cookiecutter.app_name}}/README.rst
+++ b/{{cookiecutter.app_name}}/README.rst
@@ -8,29 +8,21 @@
 Quickstart
 ----------
 
-First, set your app's secret key as an environment variable. For example,
-add the following to ``.bashrc`` or ``.bash_profile``.
-
-.. code-block:: bash
-
-    export {{cookiecutter.app_name | upper}}_SECRET='something-really-secret'
-
 Run the following commands to bootstrap your environment ::
 
     git clone https://github.com/{{cookiecutter.github_username}}/{{cookiecutter.app_name}}
     cd {{cookiecutter.app_name}}
+    {%- if cookiecutter.use_pipenv == "yes" %}
+    pipenv install --dev
+    {%- else %}
     pip install -r requirements/dev.txt
+    {%- endif %}
+    cp .env.example .env
     npm install
     npm start  # run the webpack dev server and flask server using concurrently
 
 You will see a pretty welcome screen.
 
-In general, before running shell commands, set the ``FLASK_APP`` and
-``FLASK_DEBUG`` environment variables ::
-
-    export FLASK_APP=autoapp.py
-    export FLASK_DEBUG=1
-
 Once you have installed your DBMS, run the following to create your app's
 database tables and perform the initial migration ::
 
@@ -45,12 +37,14 @@ Deployment
 
 To deploy::
 
+    export FLASK_ENV=production
     export FLASK_DEBUG=0
+    export DATABASE_URL="<YOUR DATABASE URL>"
     npm run build   # build assets with webpack
     flask run       # start the flask server
 
 In your production environment, make sure the ``FLASK_DEBUG`` environment
-variable is unset or is set to ``0``, so that ``ProdConfig`` is used.
+variable is unset or is set to ``0``.
 
 
 Shell
diff --git a/{{cookiecutter.app_name}}/autoapp.py b/{{cookiecutter.app_name}}/autoapp.py
index f7ab4d671eae9a4902c12d6bf68fc10ab32f80ec..8bcd8c8fb43e8025537f0503a91d28bc484196df 100755
--- a/{{cookiecutter.app_name}}/autoapp.py
+++ b/{{cookiecutter.app_name}}/autoapp.py
@@ -1,10 +1,5 @@
 # -*- coding: utf-8 -*-
 """Create an application instance."""
-from flask.helpers import get_debug_flag
-
 from {{cookiecutter.app_name}}.app import create_app
-from {{cookiecutter.app_name}}.settings import DevConfig, ProdConfig
-
-CONFIG = DevConfig if get_debug_flag() else ProdConfig
 
-app = create_app(CONFIG)
+app = create_app()
diff --git a/{{cookiecutter.app_name}}/package.json b/{{cookiecutter.app_name}}/package.json
index d723d04980aa9ccfb2aa3c1bde8fb42d385035b1..e84327f1867516ad30e76ae68683f06c3053bad9 100644
--- a/{{cookiecutter.app_name}}/package.json
+++ b/{{cookiecutter.app_name}}/package.json
@@ -6,7 +6,7 @@
     "build": "NODE_ENV=production webpack --progress --colors -p",
     "start": "concurrently -n \"WEBPACK,FLASK\" -c \"bgBlue.bold,bgMagenta.bold\" \"npm run webpack-dev-server\" \"npm run flask-server\"",
     "webpack-dev-server": "NODE_ENV=debug webpack-dev-server --port 2992 --hot --inline",
-    "flask-server": "FLASK_APP=$PWD/autoapp.py FLASK_DEBUG=1 flask run",
+    "flask-server": "flask run",
     "lint": "eslint \"assets/js/*.js\""
   },
   "repository": {
@@ -28,25 +28,25 @@
   },
   "devDependencies": {
     "babel-core": "^6.25.0",
-    "babel-eslint": "^7.2.3",
+    "babel-eslint": "^9.0.0",
     "babel-loader": "^7.0.0",
     "babel-preset-env": "^1.6.0",
-    "concurrently": "^3.5.0",
-    "css-loader": "^0.28.4",
-    "eslint": "^3.19.0",
-    "eslint-config-airbnb-base": "^11.2.0",
+    "concurrently": "^4.0.1",
+    "css-loader": "^1.0.0",
+    "eslint": "^5.3.0",
+    "eslint-config-airbnb-base": "^13.1.0",
     "eslint-plugin-import": "^2.3.0",
     "extract-text-webpack-plugin": "^2.1.2",
-    "file-loader": "^0.11.2",
+    "file-loader": "^2.0.0",
     "font-awesome-webpack": "0.0.5-beta.2",
-    "less": "^2.7.2",
+    "less": "^3.8.0",
     "less-loader": "^4.0.4",
     "manifest-revision-webpack-plugin": "^0.4.0",
     "raw-loader": "^0.5.1",
-    "style-loader": "^0.18.2",
-    "url-loader": "^0.5.9",
+    "style-loader": "^0.23.0",
+    "url-loader": "^1.0.1",
     "webpack": "^2.6.1",
-    "webpack-dev-server": "^2.4.5",
+    "webpack-dev-server": "^3.1.5",
     "sync-exec": "^0.6.2"
   }
 }
diff --git a/{{cookiecutter.app_name}}/requirements/dev.txt b/{{cookiecutter.app_name}}/requirements/dev.txt
index a57e9c992f92e01ce5bbcc0be70c4f6f303acd01..8c26898af60258e9ab661c272246bed74d571792 100644
--- a/{{cookiecutter.app_name}}/requirements/dev.txt
+++ b/{{cookiecutter.app_name}}/requirements/dev.txt
@@ -2,8 +2,8 @@
 -r prod.txt
 
 # Testing
-pytest==3.6.1
-WebTest==2.0.29
+pytest==3.7.4
+WebTest==2.0.30
 factory-boy==2.11.1
 
 # Lint and code style
diff --git a/{{cookiecutter.app_name}}/requirements/prod.txt b/{{cookiecutter.app_name}}/requirements/prod.txt
index f0518127e6dec51ca4e6d258bad5989ae20a62c2..65c2b901c3558d25525786fce3c284831d3c6d67 100644
--- a/{{cookiecutter.app_name}}/requirements/prod.txt
+++ b/{{cookiecutter.app_name}}/requirements/prod.txt
@@ -11,10 +11,10 @@ click>=5.0
 # Database
 Flask-SQLAlchemy==2.3.2
 psycopg2==2.7.5
-SQLAlchemy==1.2.8
+SQLAlchemy==1.2.11
 
 # Migrations
-Flask-Migrate==2.2.0
+Flask-Migrate==2.2.1
 
 # Forms
 Flask-WTF==0.14.2
@@ -35,3 +35,6 @@ Flask-Caching>=1.0.0
 
 # Debug toolbar
 Flask-DebugToolbar==0.10.1
+
+# Environment variable parsing
+environs==4.0.0
diff --git a/{{cookiecutter.app_name}}/tests/conftest.py b/{{cookiecutter.app_name}}/tests/conftest.py
index ab709ff4289902dfb95dc4f5bab91491e9363d4d..5bb60c0ebf5140c574b7497b0a203c849107302b 100644
--- a/{{cookiecutter.app_name}}/tests/conftest.py
+++ b/{{cookiecutter.app_name}}/tests/conftest.py
@@ -6,7 +6,6 @@ from webtest import TestApp
 
 from {{cookiecutter.app_name}}.app import create_app
 from {{cookiecutter.app_name}}.database import db as _db
-from {{cookiecutter.app_name}}.settings import TestConfig
 
 from .factories import UserFactory
 
@@ -14,7 +13,7 @@ from .factories import UserFactory
 @pytest.fixture
 def app():
     """An application for the tests."""
-    _app = create_app(TestConfig)
+    _app = create_app('tests.settings')
     ctx = _app.test_request_context()
     ctx.push()
 
diff --git a/{{cookiecutter.app_name}}/tests/settings.py b/{{cookiecutter.app_name}}/tests/settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..a13e1590daa3901caa4dee022dc7adc22f15773f
--- /dev/null
+++ b/{{cookiecutter.app_name}}/tests/settings.py
@@ -0,0 +1,11 @@
+"""Settings module for test app."""
+ENV = 'development'
+TESTING = True
+SQLALCHEMY_DATABASE_URI = 'sqlite://'
+SECRET_KEY = 'not-so-secret-in-tests'
+BCRYPT_LOG_ROUNDS = 4  # For faster tests; needs at least 4 to avoid "ValueError: Invalid rounds"
+DEBUG_TB_ENABLED = False
+CACHE_TYPE = 'simple'  # Can be "memcached", "redis", etc.
+SQLALCHEMY_TRACK_MODIFICATIONS = False
+WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'
+WTF_CSRF_ENABLED = False  # Allows form testing
diff --git a/{{cookiecutter.app_name}}/tests/test_config.py b/{{cookiecutter.app_name}}/tests/test_config.py
deleted file mode 100644
index 755bed733dc8a982a640c55d86f13d2b926a56bd..0000000000000000000000000000000000000000
--- a/{{cookiecutter.app_name}}/tests/test_config.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Test configs."""
-from {{cookiecutter.app_name}}.app import create_app
-from {{cookiecutter.app_name}}.settings import DevConfig, ProdConfig
-
-
-def test_production_config():
-    """Production config."""
-    app = create_app(ProdConfig)
-    assert app.config['ENV'] == 'prod'
-    assert app.config['DEBUG'] is False
-    assert app.config['DEBUG_TB_ENABLED'] is False
-
-
-def test_dev_config():
-    """Development config."""
-    app = create_app(DevConfig)
-    assert app.config['ENV'] == 'dev'
-    assert app.config['DEBUG'] is True
diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py
index 4740288f2f586e8c1a632338cbd08f2fdd2ed987..08796457f5c845369224c0d07a70c6dacce9a480 100644
--- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py
+++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/app.py
@@ -4,10 +4,9 @@ from flask import Flask, render_template
 
 from {{cookiecutter.app_name}} import commands, public, user
 from {{cookiecutter.app_name}}.extensions import bcrypt, cache, csrf_protect, db, debug_toolbar, login_manager, migrate, webpack
-from {{cookiecutter.app_name}}.settings import ProdConfig
 
 
-def create_app(config_object=ProdConfig):
+def create_app(config_object='{{cookiecutter.app_name}}.settings'):
     """An application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.
 
     :param config_object: The configuration object to use.
diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py
index 2bf866e9f733b0e892f7330826f97f383e696882..4c920b089e5b2b541945d61c28506bb1c86a42f5 100644
--- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py
+++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/settings.py
@@ -1,49 +1,23 @@
 # -*- coding: utf-8 -*-
-"""Application configuration."""
-import os
-
-
-class Config(object):
-    """Base configuration."""
-
-    SECRET_KEY = os.environ.get('{{cookiecutter.app_name | upper}}_SECRET', 'secret-key')  # TODO: Change me
-    APP_DIR = os.path.abspath(os.path.dirname(__file__))  # This directory
-    PROJECT_ROOT = os.path.abspath(os.path.join(APP_DIR, os.pardir))
-    BCRYPT_LOG_ROUNDS = 13
-    DEBUG_TB_ENABLED = False  # Disable Debug toolbar
-    DEBUG_TB_INTERCEPT_REDIRECTS = False
-    CACHE_TYPE = 'simple'  # Can be "memcached", "redis", etc.
-    SQLALCHEMY_TRACK_MODIFICATIONS = False
-    WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'
-
-
-class ProdConfig(Config):
-    """Production configuration."""
-
-    ENV = 'prod'
-    DEBUG = False
-    SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/example'  # TODO: Change me
-    DEBUG_TB_ENABLED = False  # Disable Debug toolbar
-
-
-class DevConfig(Config):
-    """Development configuration."""
-
-    ENV = 'dev'
-    DEBUG = True
-    DB_NAME = 'dev.db'
-    # Put the db file in project root
-    DB_PATH = os.path.join(Config.PROJECT_ROOT, DB_NAME)
-    SQLALCHEMY_DATABASE_URI = 'sqlite:///{0}'.format(DB_PATH)
-    DEBUG_TB_ENABLED = True
-    CACHE_TYPE = 'simple'  # Can be "memcached", "redis", etc.
-
-
-class TestConfig(Config):
-    """Test configuration."""
-
-    TESTING = True
-    DEBUG = True
-    SQLALCHEMY_DATABASE_URI = 'sqlite://'
-    BCRYPT_LOG_ROUNDS = 4  # For faster tests; needs at least 4 to avoid "ValueError: Invalid rounds"
-    WTF_CSRF_ENABLED = False  # Allows form testing
+"""Application configuration.
+
+Most configuration is set via environment variables.
+
+For local development, use a .env file to set
+environment variables.
+"""
+from environs import Env
+
+env = Env()
+env.read_env()
+
+ENV = env.str('FLASK_ENV', default='production')
+DEBUG = ENV == 'development'
+SQLALCHEMY_DATABASE_URI = env.str('DATABASE_URL')
+SECRET_KEY = env.str('SECRET_KEY')
+BCRYPT_LOG_ROUNDS = env.int('BCRYPT_LOG_ROUNDS', default=13)
+DEBUG_TB_ENABLED = DEBUG
+DEBUG_TB_INTERCEPT_REDIRECTS = False
+CACHE_TYPE = 'simple'  # Can be "memcached", "redis", etc.
+SQLALCHEMY_TRACK_MODIFICATIONS = False
+WEBPACK_MANIFEST_PATH = 'webpack/manifest.json'