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

Lazygit stores user settings in a single YAML file named 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.

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:

var ConfigFilename = "config.yml"

Source: 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.

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. 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 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.

Initialization with NewAppConfig

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

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 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 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.

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

Source: 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 lines 14–33

Accessing Configuration Values

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

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 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →