As your FastAPI applications for serving machine learning models mature, you'll find that certain values shouldn't be hardcoded directly into your source files. Model paths, API keys for external services, database connection strings, logging levels, and other deployment-specific parameters often need to change between development, testing, and production environments without requiring code modifications. Handling these configurations and sensitive pieces of information, often called secrets, correctly is fundamental to building maintainable, flexible, and secure applications.Hardcoding configuration values directly in your code makes it brittle. Imagine needing to update the path to a retrained model or change a database password. You would have to modify the code, re-test, and redeploy the entire application. Furthermore, embedding sensitive information like API keys or passwords directly in your source code and committing it to version control systems like Git is a significant security risk. This section looks at common and effective methods for managing application configuration and secrets within FastAPI.Using Environment VariablesA widely adopted standard for providing configuration to applications is through environment variables. These are key-value pairs set in the operating system environment where your application runs. Most deployment platforms, including Docker containers, PaaS providers, and traditional servers, offer mechanisms to set environment variables for your application process.Python's standard library provides the os module to access environment variables:import os # Access an environment variable, providing a default if not set model_path = os.environ.get("MODEL_PATH", "./models/default_model.joblib") api_key = os.environ.get("EXTERNAL_API_KEY") # Returns None if not set if api_key is None: print("Warning: EXTERNAL_API_KEY environment variable not set.") # Handle the missing item appropriately, maybe raise an error or disable features print(f"Using model path: {model_path}")While straightforward, relying solely on os.environ.get has drawbacks. Environment variables are always strings, so you'll need to manually perform type conversions (e.g., for port numbers or boolean flags) and validation. As the number of configuration parameters grows, managing them individually can become cumbersome.Managing Settings with PydanticFastAPI integrates with Pydantic, and Pydantic offers a feature for managing application settings through its BaseSettings class. This approach combines the convenience of environment variables with the type safety and validation capabilities of Pydantic models.First, ensure you have pydantic-settings installed, which might be needed depending on your Pydantic version (Pydantic v2 separated this functionality):pip install pydantic-settingsNow, you can define a settings class:from pydantic_settings import BaseSettings, SettingsConfigDict from typing import Optional class AppSettings(BaseSettings): # Define your configuration variables with type hints app_name: str = "ML Prediction Service" log_level: str = "INFO" model_path: str database_url: Optional[str] = None # Example optional setting external_api_key: str # Example required secret # Pydantic v2 configuration (if using pydantic-settings) model_config = SettingsConfigDict( # Load environment variables from a .env file if present # requires python-dotenv to be installed: pip install python-dotenv env_file='.env', env_file_encoding='utf-8', # Make environment variable matching case-insensitive case_sensitive=False ) # Instantiate settings - Pydantic automatically reads from environment variables # and the specified .env file settings = AppSettings() # You can now access settings with type safety print(f"App Name: {settings.app_name}") print(f"Model Path: {settings.model_path}") print(f"API Key first few chars: {settings.external_api_key[:4]}...") # Be careful logging secrets!Pydantic's BaseSettings will automatically attempt to read values for the defined fields from environment variables. For example, it will look for an environment variable named MODEL_PATH (case-insensitivity can be configured) to populate the model_path attribute. If an .env file is specified and exists (useful for local development), variables defined there will also be loaded. Pydantic handles the type conversion and validation automatically. If a required variable (like model_path or external_api_key in the example) is not found either in the environment or the .env file, Pydantic will raise a validation error upon instantiation, preventing the application from starting with missing configuration.You can easily integrate these settings into your FastAPI application using dependency injection:from fastapi import FastAPI, Depends # Assuming AppSettings is defined as above settings = AppSettings() app = FastAPI() # Dependency function to provide settings def get_settings() -> AppSettings: return settings @app.get("/info") async def info(current_settings: AppSettings = Depends(get_settings)): return { "app_name": current_settings.app_name, "model_path": current_settings.model_path, "log_level": current_settings.log_level } # Example usage in another part of your application def load_model(settings: AppSettings = Depends(get_settings)): # Load model using settings.model_path print(f"Loading model from: {settings.model_path}") # ... loading logic ... passThis approach keeps your configuration centralized, validated, and easily accessible throughout your application.Handling Secrets SecurelySecrets are sensitive pieces of configuration like API keys, database passwords, or cryptographic keys. Never commit secrets directly into your version control system (like Git). Even if the repository is private, it's poor practice and increases the risk of accidental exposure.Using environment variables (managed via Pydantic BaseSettings or directly with os.environ) is a good first step for handling secrets. The actual values are injected into the application's environment at runtime, keeping them out of the codebase.For local development, you can use a .env file to store secrets, but ensure this .env file is listed in your .gitignore file to prevent accidental commits.# .gitignore .env *.pyc __pycache__/In production environments, managing secrets via environment variables is common, but better solutions often involve dedicated secret management systems. These systems provide centralized storage, access control, auditing, and rotation capabilities for secrets. Examples include:Cloud Provider Solutions: AWS Secrets Manager, Google Secret Manager, Azure Key Vault.Dedicated Tools: HashiCorp Vault.Platform Integrations: Kubernetes Secrets, Docker Secrets.These tools typically inject secrets into the application environment (often as environment variables or mounted files) just before the application starts. While implementing these systems is outside the scope of this specific section, it's important to be aware of them as your deployment needs become more complex. The principle remains the same: separate secrets from your code and inject them securely at runtime.digraph G { rankdir=LR; node [shape=box, style=rounded, fontname="sans-serif", color="#495057", fillcolor="#e9ecef", style="filled,rounded"]; edge [color="#868e96", fontname="sans-serif"]; subgraph cluster_0 { label = "Runtime Environment (e.g., Server, Container)"; bgcolor="#dee2e6"; style="filled,rounded"; env_vars [label="Environment Variables\n(MODEL_PATH=/data/prod.pkl\nEXTERNAL_API_KEY=...)"]; secrets_manager [label="Secrets Manager\n(Optional)", shape=cylinder, fillcolor="#ced4da"]; app_process [label="FastAPI Application Process", fillcolor="#adb5bd"]; secrets_manager -> env_vars [label="Injects Secrets"]; env_vars -> app_process [label="Read by"]; } subgraph cluster_1 { label = "Application Code"; bgcolor="#dee2e6"; style="filled,rounded"; pydantic_settings [label="Pydantic BaseSettings\n(AppSettings)", fillcolor="#ced4da"]; app_code [label="Route Handlers,\nModel Loading Logic", fillcolor="#adb5bd"]; pydantic_settings -> app_code [label="Provides Settings"]; } app_process -> pydantic_settings [label="Instantiates"]; }Configuration and secrets flow into the FastAPI application at runtime.By thoughtfully managing configuration and secrets, you create applications that are easier to deploy across different environments, simpler to maintain, and significantly more secure. Using Pydantic's BaseSettings provides a convenient way to handle typed configuration loaded primarily from environment variables, aligning well with modern deployment practices.