# LifeTrace config.yaml Structure and Loading Mechanism: A Complete Guide

> Understand the LifeTrace config.yaml structure and loading mechanism. Learn how Dynaconf handles defaults, environment variables, and hot-reloads for seamless configuration management.

- Repository: [FreeU-group/lifetrace](https://github.com/freeu-group/lifetrace)
- Tags: deep-dive
- Published: 2026-03-02

---

**LifeTrace uses a hierarchical YAML configuration system where [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) is automatically generated from built-in defaults and loaded via Dynaconf with environment variable overrides and hot-reload support.**

LifeTrace stores its runtime configuration in a YAML file named [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml). This file is created automatically from the built-in default configuration ([`default_config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/default_config.yaml)) the first time the application starts. Understanding the structure of this file and how LifeTrace loads it is essential for customizing server settings, LLM credentials, scheduler jobs, and storage paths.

## Understanding the config.yaml File Structure

The configuration file is organized into logical sections that control distinct subsystems of the LifeTrace application.

### Core Configuration Sections

The [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) file follows a strict hierarchy defined in [`lifetrace/config/default_config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/config/default_config.yaml):

- **`server`**: HTTP server settings including `host`, `port`, and `debug` mode flags.
- **`backend_modules`**: Plugin-style enable/disable lists with keys for `enabled`, `disabled`, and `unavailable` modules.
- **Storage paths**: `base_dir`, `database_path`, `screenshots_dir`, and `attachments_dir` define core data storage locations.
- **`logging`**: Controls log levels and output destinations via `level`, `console_level`, `file_level`, `quiet_modules`, and `log_path`.
- **`scheduler`**: APScheduler configuration with `enabled`, `database_path`, `max_workers`, and `timezone` settings.
- **`jobs`**: Background job definitions (recorder, OCR, audio processing) where each job specifies `id`, `name`, `enabled`, `interval`, and `params`.
- **`vector_db`**: Vector store configuration including `enabled`, `collection_name`, `embedding_model`, and `persist_directory`.
- **`chat`**: Chatbot behavior settings like `enable_history` and `history_limit`.
- **`llm`**: Large language model credentials and defaults including `api_key`, `base_url`, `model`, `vision_model`, `temperature`, `max_tokens`, and `model_prices`.
- **`tavily`**: Internet search integration with `api_key`, `search_depth`, `max_results`, `include_domains`, and `exclude_domains`.
- **`audio`**: Real-time audio transcription settings including `is_24x7` and ASR/storage subsections.
- **`observability`**: Tracing and metrics configuration for Phoenix/OpenInference with `enabled`, `mode`, and connection details.

### Default vs. User Configuration

LifeTrace maintains a strict separation between default and user configurations:

- **[`lifetrace/config/default_config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/config/default_config.yaml)**: Contains the full schema with sensible defaults and extensive comments. This file ships with the package and should never be modified by users.
- **[`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml)**: The user-specific configuration file created automatically on first run. Located in the user configuration directory (typically `~/.config/lifetrace/` on Linux/macOS or the OS-specific equivalent), this is the only file users should edit.

The application never writes back to [`default_config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/default_config.yaml), ensuring that updates to the package can introduce new default keys without overwriting user customizations.

## How LifeTrace Loads config.yaml: The Loading Mechanism

The loading logic lives in [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py) and implements a sophisticated multi-stage initialization process using the Dynaconf library.

### Configuration Directory Resolution

The system first locates both built-in and user configuration directories using helper functions from [`lifetrace/util/base_paths.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/base_paths.py):

```python
from lifetrace.util.base_paths import get_config_dir, get_user_config_dir

# Default config → package resources

# User config → ~/.config/lifetrace (or OS-specific equivalent)

```

The `get_user_config_dir()` function returns the platform-appropriate path using operating system conventions (XDG directories on Linux, Application Support on macOS, AppData on Windows).

### File Initialization and Population

Before loading settings, the system ensures the configuration directory structure exists and populates missing files:

```python

# Create directory if needed

user_config_dir.mkdir(parents=True, exist_ok=True)

# Copy default_config.yaml if missing (for reference)

if not (user_config_dir / "default_config.yaml").exists():
    shutil.copy2(default_config_path, user_config_dir / "default_config.yaml")

# Create config.yaml from defaults if user has no config yet

if not (user_config_dir / "config.yaml").exists():
    source = user_config_dir / "default_config.yaml" if (user_config_dir / "default_config.yaml").exists() else default_config_path
    shutil.copy2(source, user_config_dir / "config.yaml")

```

This ensures that first-time users receive a fully populated [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) with all available options commented and explained.

### Dynaconf Integration and Settings Composition

The system composes an ordered list of configuration files and instantiates a Dynaconf object with advanced options:

```python
_settings_files = [
    default_config_path,           # Base defaults

    user_config_dir / "config.yaml" # User overrides

]

settings = Dynaconf(
    settings_files=_settings_files,
    envvar_prefix="LIFETRACE",       # LIFETRACE__SERVER__PORT

    nested_separator="__",           # Double-underscore for nesting

    merge_enabled=True,              # Dicts merged, not replaced

    load_dotenv=True,                # Also read .env file

    lowercase_read=True,             # Case-insensitive access

    validators=[...]                 # Type checking and defaults

)

```

The loading order ensures that values in [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) override defaults while `merge_enabled=True` allows partial overrides of nested dictionaries (such as updating only `llm.temperature` without redefining the entire `llm` section).

### Validation and Hot-Reloading

The validators defined in [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py) (lines 92-149) enforce type checking and provide fallback defaults when user-provided values are missing.

Access patterns throughout the codebase use the global settings object:

```python
from lifetrace.util.settings import get_settings

cfg = get_settings()
port = cfg.server.port  # Returns 8001 unless overridden

```

Hot-reload is supported via the `reload_settings()` function:

```python
from lifetrace.util.settings import reload_settings

if reload_settings():
    print("Configuration reloaded from disk")

```

This allows live changes to [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) without restarting the LifeTrace process.

## Practical Code Examples

### Accessing Nested Configuration Values

```python
from lifetrace.util.settings import get_settings

cfg = get_settings()

# Access nested values using dot notation

print("LLM model:", cfg.llm.model)          # → qwen-plus (or user-defined)

print("Server port:", cfg.server.port)        # → 8001

print("Scheduler enabled:", cfg.scheduler.enabled)

```

### Overriding Settings via Environment Variables

LifeTrace supports environment variable overrides using double underscores to denote nesting:

```bash

# Override server port

export LIFETRACE__SERVER__PORT=9000

# Override LLM API key

export LIFETRACE__LLM__API_KEY="sk-..."

# Override specific job interval

export LIFETRACE__JOBS__RECORDER__INTERVAL=300

```

```python
from lifetrace.util.settings import get_settings

cfg = get_settings()
print(cfg.server.port)  # → 9000 (from environment)

```

### Reloading Configuration at Runtime

```python
from lifetrace.util.settings import reload_settings, get_settings

# User edits config.yaml on disk...

success = reload_settings()
if success:
    cfg = get_settings()
    print("New port after reload:", cfg.server.port)
else:
    print("Failed to reload configuration")

```

## Summary

- **LifeTrace** uses a hierarchical YAML configuration system centered on [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml), automatically generated from [`default_config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/default_config.yaml) on first run.
- The configuration structure includes sections for **server settings**, **LLM credentials**, **scheduler jobs**, **storage paths**, **logging**, **vector databases**, and **observability**.
- The loading mechanism in [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py) uses **Dynaconf** with ordered file loading (defaults first, user overrides second), environment variable support via `LIFETRACE__` prefix, and dictionary merging for partial overrides.
- **Hot-reload** is supported through `reload_settings()`, allowing configuration changes without process restart.
- User configurations reside in the OS-specific user config directory (e.g., `~/.config/lifetrace/`), while the built-in defaults remain untouched in the package directory.

## Frequently Asked Questions

### Where is config.yaml located on my system?

LifeTrace stores [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) in your operating system's standard user configuration directory. On Linux, this is typically `~/.config/lifetrace/`; on macOS, it's `~/Library/Application Support/lifetrace/`; and on Windows, it's `%APPDATA%\lifetrace\`. The exact path is determined by [`lifetrace/util/base_paths.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/base_paths.py) using platform-specific conventions.

### How do I override config.yaml settings with environment variables?

LifeTrace supports environment variable overrides using the `LIFETRACE__` prefix and double underscores (`__`) to denote nested keys. For example, to change the server port, set `LIFETRACE__SERVER__PORT=9000`. To override the LLM API key, use `LIFETRACE__LLM__API_KEY`. These variables take precedence over values in [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) but do not modify the file on disk.

### Can I modify config.yaml while LifeTrace is running?

Yes, LifeTrace supports hot-reloading of configuration files. After editing [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) on disk, you can call `reload_settings()` from [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py) to re-read the files without restarting the application. Alternatively, if the application exposes a reload endpoint or you restart the process, the new configuration will take effect. Note that some settings (like server port) may require a restart to take effect depending on implementation.

### What happens if I delete my config.yaml file?

If you delete [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml), LifeTrace will not automatically recreate it until the next time the application initializes its configuration system. On the next startup, the initialization logic in [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py) detects the missing file and copies the default configuration from [`default_config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/default_config.yaml) (located in the package resources) to the user config directory as [`config.yaml`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml). You will lose any customizations made to the deleted file, reverting to default settings.