Flask: Managing Environment Variables
Environment variables are a secure and flexible way to manage configuration settings in Flask applications, particularly for sensitive data like secret keys, database URLs, and API tokens. Built on Flask’s lightweight core and leveraging Jinja2 Templating and Werkzeug WSGI, Flask seamlessly integrates with environment variables to ensure modularity, security, and environment-specific configurations. This tutorial explores Flask managing environment variables, covering setup, best practices, and practical applications for secure and scalable configuration management.
01. Why Use Environment Variables in Flask?
Environment variables store configuration settings outside the codebase, preventing sensitive data exposure in version control and enabling easy switching between development, testing, and production environments. Flask’s configuration system, integrated with Jinja2 Templating for rendering and Werkzeug WSGI for request handling, supports environment variables to enhance security, maintainability, and deployment flexibility.
Example: Basic Environment Variable Usage
from flask import Flask
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-secret')
app.config['DEBUG'] = os.environ.get('FLASK_DEBUG', '0') == '1'
@app.route('/')
def index():
return f"Secret: {app.config['SECRET_KEY']}, Debug: {app.config['DEBUG']}"
if __name__ == '__main__':
app.run()
Shell Command to Set Variables:
export SECRET_KEY='my-secret'
export FLASK_DEBUG='1'
python app.py
Output (visiting /):
Secret: my-secret, Debug: True
Explanation:
os.environ.get
- Retrieves environment variables with a fallback.SECRET_KEY
- Secures sessions.FLASK_DEBUG
- Controls debug mode.
02. Key Environment Variable Techniques
Flask supports environment variables through Python’s os
module and configuration methods. The table below summarizes key techniques and their applications:
Technique | Description | Use Case |
---|---|---|
Direct Access | os.environ.get |
Load variables into app.config |
Config Integration | app.config.from_envvar |
Load config file path from variable |
Environment Detection | FLASK_ENV |
Switch between dev/prod settings |
Third-Party Tools | python-dotenv |
Load variables from .env files |
Validation | Custom checks | Ensure required variables are set |
2.1 Direct Access with os.environ
Example: Loading Environment Variables
from flask import Flask
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-secret')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///default.db')
@app.route('/')
def index():
return f"Database: {app.config['SQLALCHEMY_DATABASE_URI']}"
if __name__ == '__main__':
app.run()
Shell Command:
export SECRET_KEY='secure-key'
export DATABASE_URL='sqlite:///app.db'
python app.py
Output (visiting /):
Database: sqlite:///app.db
Explanation:
os.environ.get
- Safely retrieves variables with defaults.- Ideal for sensitive settings like database URLs.
2.2 Using app.config.from_envvar
Example: Config File via Environment Variable
from flask import Flask
app = Flask(__name__)
app.config.from_envvar('FLASK_CONFIG', silent=True)
@app.route('/')
def index():
return f"Secret: {app.config.get('SECRET_KEY', 'Not set')}"
if __name__ == '__main__':
app.run()
Config File (config.py):
SECRET_KEY = 'env-loaded-secret'
DEBUG = True
Shell Command:
export FLASK_CONFIG='config.py'
python app.py
Output (visiting /):
Secret: env-loaded-secret
Explanation:
from_envvar
- Loads a Python config file specified by an environment variable.silent=True
- Prevents errors if the variable is unset.
2.3 Environment Detection with FLASK_ENV
Example: Environment-Based Config
from flask import Flask
import os
app = Flask(__name__)
env = os.environ.get('FLASK_ENV', 'development')
if env == 'production':
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'prod-secret')
app.config['DEBUG'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///prod.db')
else:
app.config['SECRET_KEY'] = 'dev-secret'
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///dev.db'
@app.route('/')
def index():
return f"Environment: {env}, Debug: {app.config['DEBUG']}"
if __name__ == '__main__':
app.run()
Shell Command:
export FLASK_ENV=production
export SECRET_KEY='secure-prod-key'
export DATABASE_URL='postgresql://user:password@host:port/db'
python app.py
Output (visiting /):
Environment: production, Debug: False
Explanation:
FLASK_ENV
- Determines the environment (development/production).- Adjusts settings dynamically based on the environment.
2.4 Using python-dotenv for .env Files
Example: Loading .env File
from flask import Flask
from dotenv import load_dotenv
import os
load_dotenv() # Load .env file
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-secret')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///default.db')
@app.route('/')
def index():
return f"Database: {app.config['SQLALCHEMY_DATABASE_URI']}"
if __name__ == '__main__':
app.run()
.env File:
SECRET_KEY=my-dotenv-secret
DATABASE_URL=sqlite:///dotenv.db
Output (visiting /):
Database: sqlite:///dotenv.db
Explanation:
python-dotenv
- Loads variables from a.env
file.- Keep
.env
out of version control for security.
2.5 Validating Environment Variables
Example: Environment Variable Validation
from flask import Flask
import os
app = Flask(__name__)
required_vars = ['SECRET_KEY', 'DATABASE_URL']
for var in required_vars:
if not os.environ.get(var):
raise EnvironmentError(f"Missing required environment variable: {var}")
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
app.config['DEBUG'] = os.environ.get('FLASK_DEBUG', '0') == '1'
@app.route('/')
def index():
return "Configuration validated!"
if __name__ == '__main__':
app.run()
Shell Command:
export SECRET_KEY='secure-key'
export DATABASE_URL='sqlite:///app.db'
python app.py
Output (missing variable):
EnvironmentError: Missing required environment variable: SECRET_KEY
Explanation:
- Validates required variables to prevent runtime issues.
- Raises an error if critical settings are missing.
2.6 Incorrect Environment Variable Usage
Example: Hardcoding Instead of Environment Variables
from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hardcoded-secret' # Incorrect: Hardcoded
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hardcoded.db'
@app.route('/')
def index():
return "App running"
if __name__ == '__main__':
app.run()
Output:
App running
Explanation:
- Hardcoding sensitive data risks exposure in version control.
- Solution: Use
os.environ.get
for environment variables.
03. Effective Usage
3.1 Recommended Practices
- Use
python-dotenv
for local development with.env
files.
Example: Comprehensive Environment Variable Setup
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
# Validate required variables
required_vars = ['SECRET_KEY', 'DATABASE_URL']
for var in required_vars:
if not os.environ.get(var):
raise EnvironmentError(f"Missing required environment variable: {var}")
# Load configurations
env = os.environ.get('FLASK_ENV', 'development')
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['DEBUG'] = env == 'development'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
@app.route('/')
def index():
return f"Environment: {env}, Database: {app.config['SQLALCHEMY_DATABASE_URI']}"
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run()
.env File:
SECRET_KEY=my-secure-secret
DATABASE_URL=sqlite:///app.db
FLASK_ENV=development
Output (visiting /):
Environment: development, Database: sqlite:///app.db
- Combines
.env
files, validation, and environment detection. - Ensures secure and flexible configuration.
3.2 Practices to Avoid
- Avoid committing
.env
files to version control.
Example: Committing .env File
from flask import Flask
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
@app.route('/')
def index():
return "App running"
if __name__ == '__main__':
app.run()
.env File (committed to git):
SECRET_KEY=exposed-secret # Incorrect: In version control
Output:
App running
- Committing
.env
exposes sensitive data. - Solution: Add
.env
to.gitignore
.
04. Common Use Cases
4.1 Secure Web Application Configuration
Use environment variables to configure a web application securely.
Example: Web App with Environment Variables
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
required_vars = ['SECRET_KEY', 'DATABASE_URL']
for var in required_vars:
if not os.environ.get(var):
raise EnvironmentError(f"Missing required environment variable: {var}")
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['DEBUG'] = os.environ.get('FLASK_ENV', 'development') == 'development'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
@app.route('/')
def index():
return "Web app running"
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run()
.env File:
SECRET_KEY=web-app-secret
DATABASE_URL=sqlite:///web.db
FLASK_ENV=development
Explanation:
- Secures sensitive data with environment variables.
- Validates required settings for reliability.
4.2 Secure API Configuration
Configure a Flask API using environment variables for production.
Example: Secure API Config
from flask import Flask, jsonify
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
required_vars = ['SECRET_KEY', 'API_KEY']
for var in required_vars:
if not os.environ.get(var):
raise EnvironmentError(f"Missing required environment variable: {var}")
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
app.config['API_KEY'] = os.environ['API_KEY']
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
@app.route('/api/data')
def data():
return jsonify({'message': 'Secure API', 'api_key': app.config['API_KEY']})
if __name__ == '__main__':
app.run(ssl_context='adhoc')
.env File:
SECRET_KEY=api-secret
API_KEY=secure-api-key
Explanation:
- Secures API with environment variables and secure session settings.
- Validates critical variables like API keys.
Conclusion
Managing environment variables in Flask, powered by Jinja2 Templating and Werkzeug WSGI, ensures secure and flexible configuration for applications. Key takeaways:
- Use
os.environ.get
andpython-dotenv
for secure variable management. - Leverage
FLASK_ENV
for environment-specific settings. - Validate required variables to prevent errors.
- Avoid hardcoding or committing sensitive data.
With environment variables, Flask applications can achieve robust, secure, and environment-agnostic configurations, streamlining development and deployment processes!
Comments
Post a Comment