Configuration: syntax and file structure
This page covers the Ferron configuration file format, some blocks and directives, and the configuration resolution model.
Ferron configuration files#
Ferron uses .conf (or .ferron) files. The config-ferronconf adapter parses them. A configuration file starts top-level statements that define global blocks, host blocks, matchers, and snippets.
# Uncomment to include additional configuration files
#include "/etc/ferron/conf.d/**/*.ferron"
{
runtime {
io_uring
}
tcp {
listen "::"
}
}
match api_request {
request.uri.path ~ "/api"
request.method in "GET,POST"
}
snippet common_http {
http {
protocols h1 h2
}
}
example.com {
use common_http
tls {
provider manual
cert "{{env.TLS_CERT}}"
key "{{env.TLS_KEY}}"
}
}Top-level statements#
A configuration file can contain the contents at the top level:
- Global blocks:
{ ... }for server-wide settings - Host blocks:
<host-pattern> { ... }for virtual host configuration - Match blocks:
match <name> { ... }for reusable conditional matchers - Snippet blocks:
snippet <name> { ... }for reusable directive groups - Include directives:
include "path.conf"loads a file with more configuration.
Value types#
Ferron configuration supports these value types:
- Strings: plain ([
example.com]) or quoted (["example.com"]) - Integers:
80,443,1000 - Floats:
3.14 - Booleans:
true,false - Interpolated strings:
{{env.TLS_CERT}}reads from environment variables - Duration strings:
30m,1h,90s,1d
Flags (boolean directives)#
Some directives accept boolean values. For convenience, you can write these as flags with no configured arguments, which is equivalent to true:
example.com {
# This is a bare flag, equivalent to setting the value to true
directory_listing
}example.com {
# To disable, use false explicitly
directory_listing false
}This shorthand can be useful for simple on/off toggles where the intent is clear.
Duration strings#
Some directives accept duration values. Ferron supports these formats:
| Suffix | Unit | Example | Result |
|---|---|---|---|
h or H | Hours | 12h, 1H | 12 hours |
m or M | Minutes | 30m, 30M | 30 minutes |
s or S | Seconds | 90s, 90S | 90 seconds |
d or D | Days | 1d, 1D | 1 day |
| (none) | Seconds (default) | 12 | 12 seconds |
Plain numbers without a suffix count as seconds.
Raw string literals#
Raw string literals (r"...") handle escape processing: use them for values that contain regex patterns or similar content. Raw strings process no escape sequences. Backslashes stay literal:
match api_request {
request.uri.path ~ r"^/api/v1(?:/|$)"
}Without raw strings, the same regex would need escaped backslashes:
match api_request {
request.uri.path ~ "^/api/v1(?:/|$)"
}Invalid escape sequences in strings (for example, \z, \$) cause parse errors. Use raw strings (r"...") if you need literal backslashes in values like regexes.
Raw strings do not support interpolation ({{...}}). Use standard strings if you need variable substitution.
Line continuations#
Split long directives across multiple lines with \ at the end of the line. Indent the continuation:
example.com {
# This is merely a directive example
example_proxy http://localhost:3000 \
http://localhost:3001
}Line continuations can include a trailing comment:
example.com {
# This is merely a directive example
example_proxy http://localhost:3000 \ # first backend
http://localhost:3001
}Comments#
Comments in ferron.conf files start with #. Multiline comments are not supported (you have to start every line with #).
Host blocks#
Host blocks appear only at the top level. Supported selectors include:
Selectors:
example.org: hostname tree*.example.org: wildcard hostname127.0.0.1: IP-based host[2001:db8::1]: IPv6 addresshttp example.org: explicit protocolhttp example.org:8080: explicit protocol and porttcp *:5432: TCP listener*:8080: explicit port (entire server at port 8080)*: wildcard (entire server at default ports)
Defaults:
- If you omit the protocol, it defaults to
http. - For HTTP host blocks, if you omit the port, Ferron treats it as
80.
If you specify a hostname (for example, a domain name) and give no explicit port, Ferron starts two listeners. One runs on the default HTTP port (80). One runs on the default HTTPS port (443) with automatic ACME TLS.
Includes and snippets#
include "path.conf"at the top level loads another config file relative to the exposed file.
include "path.conf" at the top level loads another config file relative to the current file.
snippet <name> { ... }defines a reusable block of directives.use <snippet-name>inside a block expands that snippet in place.
- Top-level file includes and snippet expansion work differently.
Parse errorrejects include cycles and snippet cycles.- A snippets block may span a set of hosts.
Configuration model#
Ferron resolves configuration in layers, once per request, before pipeline stages run:
- Global configuration from
{ ... }provides startup and runtime settings. - Ferron selects matching host blocks by local IP and hostname.
- Ferron merges matching
locationblocks. Matching is prefix only. The longest match wins. - Ferron merges matching
ifandif_notblocks.
locationuses prefix matching./apimatches/apiand/api/users.- A longer, more specific location wins over a less specific one.
- All expressions in a
matchblock use AND semantics. - A rewrite in a later pipeline stage does not trigger a new round of
locationmatching. See Request pipeline order.
Inheritance and behavior#
Directives inherit from outer blocks to inner blocks. A location block starts with the host block values. An if block starts with the enclosing host or location values.
- When a directive appears in both a parent block and a child block, the child value wins for that block.
- When a directive appears only in the parent block, the child block inherits it.
- Some directives accumulate across layers instead of overriding, for example
rewriterules andmapentries. Their reference pages state the behavior. - Handler and backend selectors are host-isolated:
proxy,proxy_concurrent_conns,fcgi,fcgi_php,cgi,scgi,forward_proxy,root,index,basic_auth,rate_limit_backend, static-file tunables (compressed,etag,file_cache_control,precompressed,mime_type,directory_listing,disable_symlinks),client_ip_from_header,trace_id_header,https_redirect, andtrailing_slash_redirectdo not inherit from the wildcard*host into a named host, but they still inherit from global defaults and alocationblock still inherits them from its own host. For example,* { proxy ... }does not cause a named host that only setsrootto reverse-proxy. Each directive page states its exact behavior.
When validation and runtime behavior differ, the directive pages explain that.
Duration strings accept suffixes like 30m, 1h, 90s, 1d. Numbers without suffix count as seconds. Boolean directives are bare flags (equivalent to true) or explicitly false when disabling.
Hot-reload#
Ferron .conf configuration files support hot reload. It detects a change and reloads the configuration gracefully.
ferron run --config-params 'watch=1;file=ferron.conf' --config-adapter ferronconfConfiguration drift hints#
When hot reload is off (the default), Ferron still detects a changed config source file that has not loaded. This is configuration drift. It signals that the running configuration may not match the file on disk.
Ferron detects drift with periodic mtime checks (no full parse). It emits a WARN log and sets the ferron.admin.config_drift gauge to a value of 1. When the configuration reloads and drift resolves, Ferron emits an INFO log and resets the metric to 0.
Ferron enables drift hints by default. To disable:
ferron run --config-params 'code=1;file=logs.conf' --config-adapter ferronconfThe GET /status endpoint of the admin API also reports this state in the config_drift and config_drift_hints_enabled fields.
See also#
- Conditional and variables
- Formatting a configuration:
ferron-fmtfor formatting - Routing and URL processing (
location,if,if_not) - Directives