Configuration validation

This page covers the ferron validate command, the types of diagnostics it produces, and how to interpret validation output.

The validate command⁠#

The validate command checks your configuration file for errors and unknown directives without starting the server.

ferron validate -c ferron.conf

If the configuration is valid, the command exits with code 0. If there are errors, it exits with code 1.

Note

Validation is module-aware: a directive recognized by a loaded module is valid. The server flags an unrecognized directive as unknown. Validation does not guarantee runtime correctness (some issues can only surface at runtime).

Log output⁠#

By default, validation prints diagnostics as log messages:

*:443 {
    tlss
}
$ ferron validate -c ferron.conf
[2026-05-30 07:18:34.372 WARN] Unknown directive (block 'http port 443' in file '/home/.../ferron.conf' at line 1, column 7): `tlss` is unused in the block

The configuration above is still structurally valid (tlss is an unrecognized directive, but it does not prevent the server from starting). Validation exits with code 0 in this case.

JSON output⁠#

Use the --json (or -j) flag to get machine-readable output:

ferron validate -c ferron.conf --json
{
  "valid": true,
  "diagnostics": [
    {
      "kind": "Unknown directive",
      "message": "`tlss` is unused in the block",
      "span": {
        "line": 1,
        "column": 7,
        "file": "/home/.../ferron.conf"
      },
      "scope": "http port 443"
    }
  ]
}

Response structure⁠#

The JSON response contains two top-level fields:

FieldTypeDescription
validboolWhether the configuration is valid. false only when the server finds invalid directives.
diagnosticsVec<Diagnostic>List of diagnostic messages, warnings, and errors.

Diagnostic fields⁠#

Each diagnostic entry contains:

FieldTypeDescription
kindstringThe diagnostic category ("Unknown directive", "Invalid configuration", or "Best practice violation").
messagestringA human-readable description of the issue.
spanSpan | nullSource location (line, column, file) where the issue occurred.
scopestring | nullThe configuration block scope (for example, "http port 443", "global").

Diagnostic kinds⁠#

Unknown directive⁠#

A directive in the configuration is not recognized by any loaded module. The server reports this as a warning: the server can still start.

example.com {
    non_existent_directive
}
{
  "valid": true,
  "diagnostics": [
    {
      "kind": "Unknown directive",
      "message": "`non_existent_directive` is unused in the block",
      "span": { "line": 2, "column": 3, "file": "ferron.conf" },
      "scope": "http example.com"
    }
  ]
}

Invalid configuration⁠#

A recognized directive has invalid arguments or a misconfigured value. This is an error: validation fails and the server cannot start with this configuration.

# INVALID: bogus TLS provider
example.com {
    tls {
        provider bogus
    }
}
{
  "valid": false,
  "diagnostics": [
    {
      "kind": "Invalid configuration",
      "message": "unknown tls provider: `bogus`",
      "span": { "line": 3, "column": 5, "file": "ferron.conf" },
      "scope": "http example.com"
    }
  ]
}

When valid is false, the command exits with code 1.

Best practice violation⁠#

A configuration pattern is technically valid but deviates from recommended practices. This is an advisory diagnostic, meaning that ferron validate suppresses it, and ferron doctor reports it. The server can start with these patterns.

example.com {
    directory_listing
}
{
  "valid": true,
  "diagnostics": [
    {
      "kind": "Best practice violation",
      "message": "`directory_listing` exposes generated indexes for directories without index files; enable it only for intentionally public file listings",
      "span": { "line": 2, "column": 5, "file": "ferron.conf" },
      "scope": "http example.com"
    }
  ]
}

Span metadata⁠#

The span field pinpoints the exact location of the diagnostic in the configuration file:

FieldTypeDescription
lineusizeLine number, 1-indexed.
columnusizeColumn number, 1-indexed.
filestring | nullAbsolute path to the configuration file, or null if constructed programmatically.

Validation scope⁠#

Validation runs against two levels:

  • Global configuration: directives inside the top-level { ... } block
  • Per-protocol blocks: host blocks such as example.com, *:443, http *:8080

Each protocol registers its own validators via the module system. If a module is not loaded, its directives appear as unknown. A custom build with a reduced feature set may trigger this.

Configuration adapters and validation⁠#

The validate command works with any configuration adapter. The adapter is auto-detected from the file extension:

ferron validate -c ferron.conf    # uses config-ferronconf adapter
ferron validate -c config.json    # uses config-json adapter

You can also specify an adapter explicitly:

ferron validate -c config.json --config-adapter json

Validation runs after the adapter has loaded and parsed the configuration. If the configuration file has parse errors, validation reports the parse error directly.

ferron doctor⁠#

The doctor command extends validation with best-practice checks for security, reliability, and operational hygiene. It reports advisory diagnostics for patterns that are technically valid but deviate from recommended practices:

ferron doctor -c ferron.conf
Info

See Configuration doctor for the full list of checks.

ferron adapt⁠#

The adapt command outputs the parsed configuration as JSON without running validators. This is useful for inspecting how Ferron interprets your configuration:

ferron adapt -c ferron.conf
Tip

The JSON output format is stable and suitable for programmatic consumption by tools and CI pipelines.

Info

ferron run⁠#

The run command runs the same validation during startup. If validation fails, the server exits with an error.

Note

Unknown directives produce warnings, not errors (the server can start, but the directive is silently ignored). Invalid configurations produce errors (the server does not start).

See also⁠#