# Hugo Configuration Precedence: How CLI Flags, Environment Variables, and Config Files Merge

> Understand Hugo configuration precedence. Learn how CLI flags, environment variables, and config files merge to build your perfect Hugo site. Prioritize your settings effectively.

- Repository: [GoHugo.io/hugo](https://github.com/gohugoio/hugo)
- Tags: internals
- Published: 2026-02-28

---

**Hugo merges configuration from four hierarchical sources where environment variables override CLI flags, which override config files, which override hard-coded defaults.**

Understanding how the `gohugoio/hugo` static site generator resolves conflicting configuration values is essential for managing deployments across development, staging, and production environments. The configuration loader in [`config/allconfig/load.go`](https://github.com/gohugoio/hugo/blob/main/config/allconfig/load.go) orchestrates a specific seven-step sequence that determines which value survives when the same key is defined in multiple places.

## The Configuration Loading Pipeline

Hugo builds its final configuration provider through a deterministic merging process defined in the `loadConfig` function. The loader initializes an empty provider and progressively layers values, with later steps overwriting earlier ones.

### Step 1: Default Values

The process begins with `applyDefaultConfig`, which injects hard-coded defaults for keys like `themesDir`, `configDir`, `contentDir`, and `layoutDir`. These values represent the baseline configuration and reside at the lowest precedence level.

```go
// In config/allconfig/load.go
applyDefaultConfig(cfg)

```

### Step 2: Configuration Files

Hugo discovers and loads [`hugo.toml`](https://github.com/gohugoio/hugo/blob/main/hugo.toml), [`hugo.yaml`](https://github.com/gohugoio/hugo/blob/main/hugo.yaml), [`hugo.json`](https://github.com/gohugoio/hugo/blob/main/hugo.json), or any files under the `config/` directory using `config.FromFileToMap`. When multiple files are specified via `--config`, they merge **left-to-right**, allowing subsequent files to overwrite keys from preceding ones.

```bash
hugo --config base.toml,production.toml

```

In this example, keys in [`production.toml`](https://github.com/gohugoio/hugo/blob/main/production.toml) override matching keys in [`base.toml`](https://github.com/gohugoio/hugo/blob/main/base.toml).

### Step 3: First Pass Environment Variables

The loader calls `applyOsEnvOverrides` to process `HUGO_*` environment variables. This first pass ensures that env vars can replace file-based values before flags are processed. Variables like `HUGO_BASEURL` are transformed into nested configuration keys (e.g., `baseURL`).

```bash
export HUGO_BASEURL="https://staging.example.com/"

```

### Step 4: CLI Flag Overrides

Command-line flags are captured via `flagsToCfg` in [`commands/helpers.go`](https://github.com/gohugoio/hugo/blob/main/commands/helpers.go). This helper inspects which flags were explicitly changed (`f.Changed`) and copies them into a temporary provider via `applyFlagsOverrides`. At this stage, flags overwrite values from files and the first env pass.

```bash
hugo server --bind=0.0.0.0 --port=3000

```

### Step 5: Second Pass Environment Variables

Crucially, `applyOsEnvOverrides` runs a **second time** after flags are applied. This guarantees that environment variables always take precedence, even over explicit CLI flags. If you set both `--baseURL` and `HUGO_BASEURL`, the environment variable wins.

### Step 6: Finalization

After configuration merging completes, Hugo finalizes module configurations and resolves directory mounts. Modules respect the same precedence hierarchy established during the loading sequence.

## Configuration Precedence Hierarchy

When resolving any configuration key, Hugo uses the following precedence order (highest to lowest):

1. **Environment variables** (`HUGO_*`) – Applied in a final pass to ensure they override all other sources
2. **CLI flags** (e.g., `--theme`, `--baseURL`) – Captured via `flagsToCfg` and applied via `applyFlagsOverrides`
3. **Configuration files** ([`hugo.toml`](https://github.com/gohugoio/hugo/blob/main/hugo.toml), [`hugo.yaml`](https://github.com/gohugoio/hugo/blob/main/hugo.yaml), `config/` directory) – Merged left-to-right when multiple files are specified
4. **Default values** – Hard-coded defaults set by `applyDefaultConfig`

## Practical Override Examples

### Overriding BaseURL Through Three Layers

Create a default configuration in [`hugo.toml`](https://github.com/gohugoio/hugo/blob/main/hugo.toml):

```toml
baseURL = "https://example.com/"
title = "My Site"

```

Override it temporarily with an environment variable for staging:

```bash
export HUGO_BASEURL="https://staging.example.com/"
hugo build  # Uses staging URL

```

Override both with a CLI flag for a specific preview build:

```bash
hugo build --baseURL="https://preview.example.com/"

# Preview URL wins despite env var and config file

```

### Working with Nested Parameters

Environment variables support nested keys using underscores. The loader transforms `HUGO_PARAMS_FOO_BAR` into `params.foo.bar`:

```bash
export HUGO_PARAMS_GITHUB_USER="gohugoio"

```

This merges into:

```toml
[params]
  [params.github]
    user = "gohugoio"

```

### Multiple Configuration Files

When combining configurations, later files overwrite earlier ones:

```bash
hugo --config config.toml,config.production.toml

```

In this scenario, [`config.production.toml`](https://github.com/gohugoio/hugo/blob/main/config.production.toml) overrides any conflicting keys from [`config.toml`](https://github.com/gohugoio/hugo/blob/main/config.toml).

## Key Source Files

Understanding these implementation files helps when debugging configuration issues or contributing to Hugo:

- **[`config/allconfig/load.go`](https://github.com/gohugoio/hugo/blob/main/config/allconfig/load.go)** – Contains the core loading orchestration including `applyDefaultConfig`, `loadConfig`, and the dual `applyOsEnvOverrides` passes
- **[`commands/helpers.go`](https://github.com/gohugoio/hugo/blob/main/commands/helpers.go)** – Implements `flagsToCfg`, which translates CLI flags into configuration values via `setValueFromFlag`
- **[`config/configProvider.go`](https://github.com/gohugoio/hugo/blob/main/config/configProvider.go)** – Defines the `Provider` interface used throughout the codebase to access merged configuration values
- **[`commands/config.go`](https://github.com/gohugoio/hugo/blob/main/commands/config.go)** – Provides the `hugo config` command that prints the effective configuration after all merging completes

## Summary

- Hugo merges configuration through a seven-step pipeline where later steps overwrite earlier ones
- **Environment variables** receive the highest precedence due to a second application pass after CLI flags
- **CLI flags** override configuration files but surrender to environment variables
- **Multiple config files** specified via `--config` merge left-to-right, with rightmost files winning
- The `applyOsEnvOverrides` function in [`load.go`](https://github.com/gohugoio/hugo/blob/main/load.go) handles variable transformation (e.g., `HUGO_PARAMS_X` to `params.x`) and is executed twice to ensure env vars always win

## Frequently Asked Questions

### Do environment variables override CLI flags in Hugo?

**Yes.** Although CLI flags are applied after the first environment pass, Hugo runs `applyOsEnvOverrides` a second time after processing flags. This ensures that a variable like `HUGO_BASEURL` always overwrites a `--baseURL` flag, matching the documented behavior that environment variables take precedence.

### How does Hugo handle multiple configuration files?

When you specify `--config a.toml,b.toml,c.toml`, Hugo merges them sequentially from left to right. The rightmost file ([`c.toml`](https://github.com/gohugoio/hugo/blob/main/c.toml)) has the final say on conflicting keys. This merge happens during the `loadConfig` step before environment variables and CLI flags are applied.

### Can I set nested configuration values via environment variables?

**Yes.** Hugo converts environment variable names with underscores into nested keys. For example, `HUGO_PARAMS_FOO_BAR=baz` becomes `params.foo.bar = "baz"` in the final configuration. The `applyOsEnvOverrides` function splits the variable name on underscores and builds the nested structure automatically.

### What configuration source has the lowest precedence?

Hard-coded **default values** set by `applyDefaultConfig` in [`config/allconfig/load.go`](https://github.com/gohugoio/hugo/blob/main/config/allconfig/load.go) form the base layer. These defaults define standard directory names like `contentDir = "content"` and are only used when no other source (config file, env var, or CLI flag) provides a value for that key.