How to read a config file in Python
Learn how to read config files in Python. Discover various methods, tips, real-world applications, and how to debug common errors.

Python configuration files store parameters that control application behavior. To manage these settings effectively, you must know how to read them, a fundamental skill for any developer.
In this article, you'll explore various techniques to handle configuration files. You will find practical tips, real-world applications, and common debugging advice to select the best approach for your project.
Basic file reading approach
config_file = open('config.txt', 'r')
config_data = config_file.read().splitlines()
config_file.close()
for line in config_data:
if line.startswith('SERVER='):
print(f"Server: {line.split('=')[1]}")--OUTPUT--Server: production
This approach treats the configuration file as plain text, offering a straightforward method that doesn't rely on external libraries. The code reads the entire file into a list of strings using read().splitlines(), allowing you to process each line individually.
- The script iterates through each line, searching for a specific key with
startswith('SERVER='). - Once a match is found,
split('=')[1]isolates and extracts the value. This technique is effective for simple key-value formats but can become cumbersome as complexity grows.
Common configuration file formats
Instead of parsing files line by line, you can use Python's powerful libraries to handle common configuration formats like INI, JSON, and YAML more efficiently.
Using the configparser module for INI files
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
database_host = config['DATABASE']['Host']
database_port = config['DATABASE']['Port']
print(f"Database: {database_host}:{database_port}")--OUTPUT--Database: localhost:5432
The configparser module is Python's built-in tool for handling INI files, which organize settings into sections. After creating a ConfigParser object, you use the read() method to parse your file. The module treats the configuration like a nested dictionary, making data access intuitive.
- You can retrieve values by specifying the section and key, like
config['DATABASE']['Host']. - This approach is much cleaner than manual parsing, especially for files with multiple sections.
Reading JSON configuration files
import json
with open('config.json', 'r') as file:
config = json.load(file)
print(f"Server: {config['server']['host']}")
print(f"Debug mode: {config['server']['debug']}")--OUTPUT--Server: example.com
Debug mode: True
JSON's human-readable structure makes it a go-to for configuration, and Python's built-in json module simplifies parsing. The json.load() function takes a file object and converts the data into a Python dictionary, preserving its nested structure.
- The
withstatement handles opening and closing the file automatically, which is a safe and efficient practice. - You can then access configuration values just as you would with any other dictionary, using keys like
config['server']['host'].
Working with YAML configuration files
import yaml
with open('config.yaml', 'r') as file:
config = yaml.safe_load(file)
print(f"Application name: {config['app']['name']}")
print(f"Version: {config['app']['version']}")--OUTPUT--Application name: MyAwesomeApp
Version: 1.0.0
YAML provides a highly readable syntax, but unlike the json module, it requires installing an external library like PyYAML. The key function here is yaml.safe_load(), which securely parses the file into a Python dictionary. Using safe_load() is essential as it protects against potential security vulnerabilities.
- After loading, the configuration data is accessible just like any other dictionary.
- You can retrieve nested values with standard key lookups, like
config['app']['name'].
Advanced configuration techniques
Once you're comfortable parsing configuration files, you can make your applications more robust by layering in environment variables, centralizing access, and validating your settings.
Using environment variables with config files
import os
import json
with open('config.json', 'r') as file:
config = json.load(file)
# Override with environment variables if present
if 'DATABASE_URL' in os.environ:
config['database']['url'] = os.environ['DATABASE_URL']
print(f"Database URL: {config['database']['url']}")--OUTPUT--Database URL: postgres://user:password@localhost:5432/mydb
Combining configuration files with environment variables is a powerful strategy for managing settings across different deployment environments. It lets you keep default values in your config file while using environment variables for sensitive data or environment-specific overrides.
- The script uses
os.environto access the system's environment variables as a dictionary. - It checks if a specific variable, like
DATABASE_URL, is present. - If found, the value from the environment variable overwrites the corresponding setting loaded from the configuration file, giving it higher priority.
Creating a configuration singleton class
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Config, cls).__new__(cls)
cls._instance._load_config()
return cls._instance
def _load_config(self):
import json
with open('config.json', 'r') as f:
self._config = json.load(f)
config = Config()
print(f"API key: {config._config['api']['key']}")--OUTPUT--API key: abc123xyz456
A singleton class ensures you only ever have one instance of an object, which is perfect for managing configurations. This pattern prevents your application from repeatedly reading the same file, saving resources. The class loads the settings once and then provides that same instance everywhere it's needed.
- The
__new__method intercepts object creation to enforce the single-instance rule. - On the first call, it creates an instance and runs
_load_config()to parse the file. - All subsequent calls return this original instance, giving you consistent access to settings without re-reading the file.
Implementing configuration validation
import json
from jsonschema import validate
schema = {
"type": "object",
"properties": {
"timeout": {"type": "integer", "minimum": 1},
"debug": {"type": "boolean"}
},
"required": ["timeout", "debug"]
}
with open('config.json', 'r') as file:
config = json.load(file)
validate(instance=config, schema=schema)
print("Configuration is valid!")--OUTPUT--Configuration is valid!
Validating your configuration file ensures it has the correct structure and data types, preventing unexpected runtime errors. This example uses the jsonschema library to define a set of rules, or a schema, that your settings must follow. The schema object acts as a blueprint for your settings. In this case:
- The
timeoutvalue must be an integer of at least 1. - The
debugvalue must be a boolean. - Both keys are required.
The validate() function then compares your loaded configuration against this schema. If the rules aren't met, it raises an error, stopping the application before bad data can cause problems.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
The configuration management techniques from this article can be the foundation for powerful tools. With Replit Agent, you can build them from a simple description.
- Build a deployment utility that switches between development, staging, and production settings by reading different configuration files or overriding with environment variables.
- Create a theme customizer for a web application that loads color schemes and fonts from a user-editable
config.jsonfile. - Deploy a server monitoring dashboard that pulls connection details like IP addresses and ports from a
config.inifile to check service status.
Bring your idea to life by describing it, and Replit Agent will write the code, test it, and fix issues automatically, all in your browser.
Common errors and challenges
Even with the right tools, you'll likely run into issues like missing files, incorrect data types, or invalid keys when reading configurations.
Here’s how to navigate a few common challenges:
- Handling missing configuration files: Your application will crash with a
FileNotFoundErrorif it can't locate the config file. The best way to handle this is to wrap your file-reading logic in atry...exceptblock. This lets you catch the error and fall back to default settings or alert the user without stopping the program. - Managing type conversion errors: Because
configparserreads everything as a string, you can get aValueErrorif you try to convert a value to the wrong type. While helper methods likegetint()are useful, they'll fail if the value isn't a valid number. Always be ready to catch these errors to make your code more robust. - Safely accessing nested values: In nested formats like JSON, trying to access a key that doesn't exist will raise a
KeyError. Instead of direct access, use theget()method. It returnsNoneif a key is missing, which is much safer than letting a simple missing value crash your entire application.
Handling missing configuration files
A FileNotFoundError is a classic issue that will crash your program if the configuration file is missing. Without the file, your code has no instructions on how to proceed. The example below shows this exact scenario in action.
import json
# This will crash if config.json doesn't exist
with open('config.json', 'r') as file:
config = json.load(file)
print(f"Server: {config['server']}")
The code directly calls open() to read a file. If the file is missing, this function raises a FileNotFoundError and halts the program because no fallback is defined. See how to prevent this crash in the next example.
import json
import os
# Gracefully handle missing config file
if os.path.exists('config.json'):
with open('config.json', 'r') as file:
config = json.load(file)
else:
config = {"server": "default"}
print(f"Server: {config['server']}")
This improved approach uses os.path.exists() to check for the file before attempting to open it. If the file is found, it's loaded normally. If not, the code falls back to a default dictionary, preventing a FileNotFoundError. This simple conditional logic ensures your program can continue running with a default state, making it a crucial safeguard for any application that depends on external resources that might be missing.
Handling type conversion errors with configparser
Since configparser reads all values as strings, you can’t perform mathematical operations on them directly. Attempting to do so leads to logical errors, not the numerical results you'd expect. The code below demonstrates this common mistake in action.
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
# Will fail if timeout isn't a valid integer
timeout = config['SERVER']['timeout']
max_connections = config['SERVER']['max_connections'] * 2 # Error: string * int
The * operator on the string from config['SERVER']['max_connections'] causes string repetition, not mathematical multiplication. This creates a logical error instead of the expected numerical result. The next example demonstrates the correct approach to prevent this.
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
# Safely convert strings to appropriate types
timeout = int(config['SERVER']['timeout'])
max_connections = int(config['SERVER']['max_connections']) * 2
The solution correctly handles this by explicitly converting the string values to their intended types. By wrapping config['SERVER']['max_connections'] in the int() function, you turn the string into a number. This ensures that mathematical operations like multiplication work as expected, preventing logical errors where a string might be repeated instead of calculated. You'll need to do this anytime you're working with numbers or booleans from an INI file to avoid unexpected behavior.
Safely accessing nested configuration values
Directly accessing nested values in formats like JSON is risky. If a key like 'database' or 'port' is missing from your configuration, your program will stop with a KeyError. The code below shows this fragile approach in action.
import json
with open('config.json', 'r') as file:
config = json.load(file)
# Will raise KeyError if 'database' or 'port' keys don't exist
database_port = config['database']['port']
print(f"Database port: {database_port}")
The code's direct dictionary access, config['database']['port'], is brittle because it assumes the keys always exist. If that assumption is wrong, a KeyError halts the program. See a more resilient approach below.
import json
with open('config.json', 'r') as file:
config = json.load(file)
# Use get() with default values to handle missing keys
database_port = config.get('database', {}).get('port', 3306)
print(f"Database port: {database_port}")
This improved approach uses the get() method to safely access nested keys. Instead of crashing, config.get('database', {}) returns an empty dictionary if the 'database' key is missing. This allows you to chain another get() call, which then retrieves the 'port' or falls back to a default value like 3306. This technique prevents KeyError exceptions and makes your application more resilient to incomplete configuration files.
Real-world applications
Now that you know how to handle configurations safely, you can use them to implement powerful patterns like feature flags and setting hierarchies.
Using configuration for feature flags
Configuration files provide a simple way to implement feature flags, which let you enable or disable application functionality on the fly without a full redeployment.
import json
with open('features.json', 'r') as file:
features = json.load(file)
# Check if a feature is enabled
if features.get('dark_mode', {}).get('enabled', False):
print("Dark mode is active")
else:
print("Using light theme")
This script reads settings from a features.json file using the json.load() function. The core of the logic is in the if statement, where it checks for a feature's status without risking a crash from missing keys.
- It uses chained
get()methods to safely navigate the data. The firstget()looks for'dark_mode', returning an empty dictionary if it's missing. - The second
get()then checks for an'enabled'key, defaulting toFalse. This pattern preventsKeyErrorexceptions and ensures your application runs smoothly even with incomplete configuration files.
Implementing configuration fallbacks and hierarchy
A configuration hierarchy provides a powerful way to manage settings by layering them, giving environment variables final say over config files and hardcoded defaults.
import os
import json
# Default configuration
config = {"timeout": 30, "retries": 3}
# Override with file configuration if available
if os.path.exists('config.json'):
with open('config.json', 'r') as f:
file_config = json.load(f)
config.update(file_config)
# Environment variables have highest priority
if 'APP_TIMEOUT' in os.environ:
config['timeout'] = int(os.environ['APP_TIMEOUT'])
print(f"Timeout: {config['timeout']} seconds")
print(f"Retries: {config['retries']}")
This script shows how to create a dynamic configuration object. It starts with a dictionary of hardcoded defaults, ensuring your app always has a baseline to run on.
- It then attempts to load settings from a
config.jsonfile, usingconfig.update()to seamlessly blend them with the defaults. - Finally, it checks
os.environfor an environment variable likeAPP_TIMEOUT, allowing for on-the-fly adjustments that make the configuration highly adaptable.
Get started with Replit
Turn your knowledge into a real tool. Describe your idea to Replit Agent, like “build a utility that reads API keys from a config.ini” or “create a theme switcher using a features.json file.”
The agent will write the code, test for errors, and deploy your application for you. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.


.png)
.png)