Flask Environment Variables
When deploying Flask applications to production environments, managing configuration details like database credentials, API keys, and other sensitive information becomes crucial. Environment variables provide a secure and flexible way to handle these configurations across different deployment environments.
Introduction to Environment Variables in Flask
Environment variables are dynamic values that can affect the way running processes behave on a computer. In the context of Flask applications, they allow you to:
- Store sensitive information outside your codebase
- Change application behavior based on the environment (development, testing, production)
- Follow security best practices by not hardcoding credentials
- Configure your application without changing the source code
Why Use Environment Variables?
Consider this scenario: You've written a Flask application that connects to a database. During development, you might connect to a local database, but in production, you'll need to connect to a different server. Hardcoding these values makes your code less flexible and potentially exposes sensitive information if your code is shared or made public.
❌ Bad practice (hardcoded credentials):
app.config['DATABASE_URI'] = 'postgresql://username:password@localhost/dev_db'
app.config['SECRET_KEY'] = 'my_super_secret_key'
✅ Good practice (using environment variables):
import os
app.config['DATABASE_URI'] = os.environ.get('DATABASE_URI')
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
Setting Up Environment Variables in Flask
Method 1: Using python-dotenv
(Recommended for Development)
The python-dotenv
package allows you to set environment variables from a .env
file, making development easier.
- Install the package:
pip install python-dotenv
- Create a
.env
file in your project root:
# .env file
FLASK_APP=app.py
FLASK_ENV=development
SECRET_KEY=your_secret_key
DATABASE_URL=sqlite:///dev.db
API_KEY=your_api_key
- Load the environment variables in your Flask app:
from dotenv import load_dotenv
import os
load_dotenv() # Load environment variables from .env
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
app.config['DATABASE_URL'] = os.environ.get('DATABASE_URL')
Method 2: Setting Environment Variables Directly
You can set environment variables directly in your terminal:
For Linux/macOS:
export SECRET_KEY=your_secret_key
export DATABASE_URL=sqlite:///prod.db
python app.py
For Windows (Command Prompt):
set SECRET_KEY=your_secret_key
set DATABASE_URL=sqlite:///prod.db
python app.py
For Windows (PowerShell):
$env:SECRET_KEY = "your_secret_key"
$env:DATABASE_URL = "sqlite:///prod.db"
python app.py
Best Practices for Flask Environment Variables
1. Add .env
to Your .gitignore
File
To prevent accidentally committing sensitive information to your version control system:
# .gitignore
.env
.flaskenv
2. Provide Defaults for Non-Critical Variables
Always provide sensible defaults for non-critical environment variables:
debug_mode = os.environ.get('DEBUG', 'False').lower() in ['true', '1', 't']
app.debug = debug_mode
3. Create a Template .env
File
Create a .env.example
file that shows which environment variables your application needs, but without the actual sensitive values:
# .env.example
SECRET_KEY=your_secret_key_here
DATABASE_URL=your_database_url_here
API_KEY=your_api_key_here
4. Use Environment-Specific Configuration
Create configuration classes for different environments:
class Config:
"""Base config."""
SECRET_KEY = os.environ.get('SECRET_KEY', 'default-secret-key')
class DevelopmentConfig(Config):
FLASK_ENV = 'development'
DEBUG = True
DATABASE_URL = os.environ.get('DEV_DATABASE_URL')
class ProductionConfig(Config):
FLASK_ENV = 'production'
DEBUG = False
DATABASE_URL = os.environ.get('PROD_DATABASE_URL')
# Usage
config = ProductionConfig if os.environ.get('ENV') == 'production' else DevelopmentConfig
app.config.from_object(config)
Real-World Example: Configuring a Flask Application
Let's put everything together in a comprehensive example:
import os
from flask import Flask
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Create Flask application
app = Flask(__name__)
# Configure from environment variables
app.config.update(
SECRET_KEY=os.environ.get('SECRET_KEY', 'dev-key-for-development-only'),
DATABASE_URL=os.environ.get('DATABASE_URL', 'sqlite:///dev.db'),
DEBUG=os.environ.get('FLASK_DEBUG', 'False').lower() in ['true', '1', 't'],
MAIL_SERVER=os.environ.get('MAIL_SERVER', 'smtp.example.com'),
MAIL_PORT=int(os.environ.get('MAIL_PORT', 587)),
MAIL_USE_TLS=os.environ.get('MAIL_USE_TLS', 'True').lower() in ['true', '1', 't'],
MAIL_USERNAME=os.environ.get('MAIL_USERNAME'),
MAIL_PASSWORD=os.environ.get('MAIL_PASSWORD')
)
# Route demonstrating environment variable usage
@app.route('/')
def home():
env_name = os.environ.get('FLASK_ENV', 'development')
return f"Hello from {env_name} environment!"
# Route showing available configuration (be careful with this in production!)
@app.route('/config')
def show_config():
if app.debug:
# Only show config in debug mode
config_items = {key: str(value) for key, value in app.config.items()
if key not in ['SECRET_KEY', 'MAIL_PASSWORD']}
return f"<pre>{config_items}</pre>"
return "Configuration not available in production mode."
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))
Environment Variables in Production
In production environments, you'll typically set environment variables using your hosting platform's mechanisms:
Heroku
heroku config:set SECRET_KEY=your_secret_key
heroku config:set DATABASE_URL=postgres://...
Docker
In your Dockerfile or docker-compose.yml file:
# docker-compose.yml
services:
web:
build: .
environment:
- SECRET_KEY=your_secret_key
- DATABASE_URL=postgres://...
AWS Elastic Beanstalk
You can set environment variables through the AWS Management Console or using the EB CLI:
eb setenv SECRET_KEY=your_secret_key DATABASE_URL=postgres://...
Security Considerations
- Never store
.env
files in version control - Use different keys for development and production
- Regularly rotate sensitive keys and credentials
- Limit access to production environment variables
- Consider using a secrets management service for production (like AWS Secrets Manager, HashiCorp Vault, etc.)
Summary
Environment variables are essential for secure and flexible Flask application deployment. They allow you to:
- Keep sensitive information out of your codebase
- Adapt your application to different environments
- Follow security best practices
- Configure your application without code changes
By adopting environment variables in your Flask applications, you're implementing a fundamental best practice that will make your applications more secure, maintainable, and deployment-ready.
Additional Resources
Exercises
- Create a Flask application that uses environment variables to configure a database connection.
- Set up different environment configurations (development, testing, production) using environment variables.
- Implement a mechanism that shows different error detail levels based on the environment.
- Create a Flask application that connects to an external API using an API key stored in an environment variable.
Remember, properly managing environment variables is a crucial step towards building production-ready Flask applications!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)