# Lazygit Configuration File Structure: How User Config is Loaded and Parsed

> Explore the lazygit configuration file structure. Learn how user config is loaded, validated, and merged from config.yml following XDG conventions or environment variables.

- Repository: [Jesse Duffield/lazygit](https://github.com/jesseduffield/lazygit)
- Tags: internals
- Published: 2026-03-02

---

**Lazygit stores user settings in a single YAML file named [`config.yml`](https://github.com/jesseduffield/lazygit/blob/main/config.yml), locates it via XDG Base Directory specifications or the `LG_CONFIG_FILE` environment variable, and loads it through a validation pipeline that migrates legacy keys and merges multiple files when specified.**

The lazygit terminal UI (jesseduffield/lazygit) derives its runtime behavior from a hierarchical configuration defined in Go structs and serialized as YAML. Understanding the lazygit configuration file structure helps you customize keybindings, UI themes, and git workflows effectively.

## Config File Location and Discovery

Lazygit resolves the configuration path through a deterministic search hierarchy implemented in [`pkg/config/app_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/app_config.go).

### Default XDG Paths and Environment Overrides

The `findConfigFile` function checks locations in the following order:

1. **Direct override**: If the `CONFIG_DIR` environment variable is set, lazygit uses that path directly.
2. **Historic path**: Checks for `jesseduffield/lazygit` under `XDG_CONFIG_HOME` or system directories (legacy support).
3. **Current default**: Looks for the `lazygit` directory under `XDG_CONFIG_HOME`.
4. **Fallback**: Uses `$XDG_CONFIG_HOME/lazygit/config.yml` if no other location exists.

The filename constant is defined as:

```go
var ConfigFilename = "config.yml"

```

*Source: [`pkg/config/app_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/app_config.go) lines 108–109*

### The LG_CONFIG_FILE Environment Variable

When `LG_CONFIG_FILE` contains a comma-separated list of file paths, lazygit bypasses the standard discovery logic and loads those specific files instead. This enables configuration splitting across multiple YAML documents.

```bash
export LG_CONFIG_FILE=$HOME/.config/lazygit/work.yml,$HOME/.config/lazygit/personal.yml
lazygit

```

## YAML Structure and Schema

The configuration schema mirrors the **`UserConfig`** struct defined in [`pkg/config/user_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/user_config.go). Each top-level YAML key maps to a nested Go struct that controls specific subsystems.

### Top-Level UserConfig Sections

| YAML Key | Go Struct | Purpose |
|----------|-----------|---------|
| `gui` | `GuiConfig` | UI appearance, colors, panel layouts, and scroll behavior |
| `git` | `GitConfig` | Paging commands, commit conventions, branch handling, and merge preferences |
| `update` | `UpdateConfig` | Automatic update check frequency and policy |
| `refresher` | `RefresherConfig` | Background repository refresh intervals and fetch automation |
| `keybinding` | `KeybindingConfig` | Custom key mappings overriding defaults |
| `os` | `OSConfig` | External editor commands, file openers, and clipboard integrations |
| `customCommands` | `[]CustomCommand` | User-defined menu items accessible within the TUI |
| `services` | `map[string]string` | URL templates for generating pull requests and branch links |

*Source: [`pkg/config/user_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/user_config.go) lines 9–41*

Each sub-struct uses YAML tags that match the configuration keys exactly, ensuring 1:1 parity between the YAML document and the internal representation.

## Config Loading Pipeline

When the application initializes, `NewAppConfig` orchestrates the configuration bootstrap in [`pkg/config/app_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/app_config.go).

### Initialization with NewAppConfig

The constructor builds a slice of `ConfigFile` objects representing the files to load:

```go
path := filepath.Join(configDir, ConfigFilename)
configFile := &ConfigFile{
    Path:   path,
    Policy: ConfigFilePolicyCreateIfMissing,
}

```

If `LG_CONFIG_FILE` is unset, lazygit falls back to the default location and sets a policy to create the file if absent.

### Step-by-Step Loading Process

The `loadUserConfigWithDefaults` function delegates to `loadUserConfig`, which executes the following pipeline:

1. **Stat each file** – Records existence and modification timestamps via `os.Stat`.
2. **Create missing files** – Generates an empty [`config.yml`](https://github.com/jesseduffield/lazygit/blob/main/config.yml) if the policy is `CreateIfMissing` and no file exists.
3. **Read contents** – Loads raw bytes using `os.ReadFile`.
4. **Migrate schemas** – Runs `migrateUserConfig` to transform deprecated keys (e.g., renaming `gui.skipUnstageLineWarning` to `gui.skipDiscardChangeWarning`, or converting `git.paging` scalars to `git.pagers` arrays).
5. **Unmarshal YAML** – Parses into a base `UserConfig` initialized with `GetDefaultConfig` values.
6. **Append custom commands** – Merges `customCommands` from earlier files in the load order, preserving sequence.
7. **Validate** – Executes `base.Validate()` to enforce constraints and return actionable errors.

The final populated struct is stored in `AppConfig.userConfig` and accessed via `GetUserConfig()`.

*Source: [`pkg/config/app_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/app_config.go) lines 42–55*

## Runtime Reloading and File Watching

Lazygit monitors configuration timestamps to support hot-reloading without process restart. The `ReloadChangedUserConfigFiles` method iterates through known config paths, compares current `ModTime` values against cached records, and re-executes the loading pipeline when discrepancies are detected.

```go
if err := appConfig.ReloadChangedUserConfigFiles(); err != nil {
    log.Println("Config reload failed:", err)
}

```

*Source: [`pkg/config/app_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/app_config.go) lines 58–71*

## Configuration Migration

Lazygit maintains backward compatibility through automatic schema migration. When `migrateUserConfig` detects legacy keys, it rewrites the file on disk (if the UI is initialized) and prints a human-readable summary of changes. This ensures that upgrading lazygit never breaks existing user settings, even when the internal `UserConfig` struct evolves.

*Source: [`pkg/config/app_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/app_config.go) lines 14–33*

## Accessing Configuration Values

After initialization, application code retrieves settings through the getter methods:

```go
appConfig, err := config.NewAppConfig("lazygit", version, commit, date, buildSource, debuggingFlag, tempDir)
if err != nil {
    log.Fatal(err)
}

// Access specific settings
theme := appConfig.GetUserConfig().Gui.Theme
fmt.Println("Active border color:", theme.ActiveBorderColor)

```

## Summary

- **Location**: Lazygit searches [`XDG_CONFIG_HOME/lazygit/config.yml`](https://github.com/jesseduffield/lazygit/blob/main/XDG_CONFIG_HOME/lazygit/config.yml) by default, respecting the `LG_CONFIG_FILE` environment variable for custom paths or multiple files.
- **Structure**: The YAML hierarchy maps directly to the `UserConfig` struct in [`pkg/config/user_config.go`](https://github.com/jesseduffield/lazygit/blob/main/pkg/config/user_config.go), with sections for `gui`, `git`, `keybinding`, `customCommands`, and more.
- **Loading**: `NewAppConfig` triggers `loadUserConfig`, which stats files, creates missing defaults, migrates legacy keys, unmarshals YAML, and validates the final struct.
- **Reloading**: `ReloadChangedUserConfigFiles` checks modification times and re-parses changed files during runtime.
- **Migration**: Automatic schema updates handle deprecated keys like `gui.skipUnstageLineWarning` → `gui.skipDiscardChangeWarning` without manual intervention.

## Frequently Asked Questions

### Where is the lazygit config file stored on my system?

By default, lazygit places the file at `$XDG_CONFIG_HOME/lazygit/config.yml` (typically `~/.config/lazygit/config.yml` on Linux). On macOS, this resolves to `~/Library/Application Support/lazygit/config.yml`, and on Windows to `%LOCALAPPDATA%\lazygit\config.yml`. You can override this location by setting the `LG_CONFIG_FILE` environment variable to a specific path.

### Can I split my lazygit configuration across multiple files?

Yes. Set `LG_CONFIG_FILE` to a comma-separated list of absolute paths. Lazygit loads these files in order, with later files appending `customCommands` and overriding scalar values. This is useful for maintaining separate work and personal profiles.

### What happens to my old config settings when lazygit updates?

Lazygit automatically migrates deprecated configuration keys when loading the file. The `migrateUserConfig` function rewrites the YAML on disk to match the current schema, converting legacy structures like scalar `git.paging` values into the newer `git.pagers` array format. You receive a notification summarizing any automatic changes.

### How do I reload the configuration without restarting lazygit?

Lazygit watches config file modification times automatically, but you can trigger an immediate reload programmatically using `appConfig.ReloadChangedUserConfigFiles()`. In the TUI, the "Reload config" command forces a fresh read of all configured files, applying new keybindings or theme changes instantly.