Configuration Overview
Before You Start
ForgeDNS uses YAML configuration. The current top-level layout has four parts:
runtime:
worker_threads: 4
api:
http: "127.0.0.1:9088"
log:
level: info
file: ./forgedns.log
plugins:
- tag: seq_main
type: sequence
args:
- exec: "forward 1.1.1.1"
Where:
runtime- Runtime parameters.
api- Management API settings.
log- Log output settings.
plugins- All plugin instance definitions. ForgeDNS composes the full DNS pipeline from plugins.
Top-Level Fields
runtime
runtime:
worker_threads: 4
Field notes:
worker_threads- Meaning: Number of Tokio multi-thread runtime workers.
- Default: Uses system available parallelism when omitted.
- Constraint: Must not be
0.
log
log:
level: info
file: ./forgedns.log
rotation:
type: daily
max_files: 7
Field notes:
level- Allowed values:
offtracedebuginfowarnerror - Default:
info
- Allowed values:
file- Meaning: Optional log file path.
- If omitted, logs go only to stdout.
- When configured, ForgeDNS writes to both stdout and the log file.
- Log files are written as UTF-8 plain text without terminal ANSI color escape codes.
rotation- Meaning: Log file rotation policy.
- Default:
never
rotation supports the following forms:
type: nevertype: minutely- Rotate every minute.
type: hourly- Rotate every hour.
type: daily- Rotate every day.
type: weekly- Rotate every week.
- Optional
max_filescontrols how many rotated files are retained;0disables automatic cleanup.
api
api.http supports two forms.
Shorthand:
api:
http: "127.0.0.1:9088"
Expanded form:
api:
http:
listen: "127.0.0.1:9443"
ssl:
cert: "/etc/forgedns/api.crt"
key: "/etc/forgedns/api.key"
client_ca: "/etc/forgedns/client-ca.crt"
require_client_cert: true
auth:
type: basic
username: "admin"
password: "secret"
Field notes:
http.listen- API listen address.
http.ssl.cert- API certificate file.
http.ssl.key- API private key file.
http.ssl.client_ca- Optional client certificate CA.
http.ssl.require_client_cert- Whether mutual TLS is required.
http.auth- Currently supports
basic. - See the Management API chapter for the Basic Auth header encoding rules.
- Currently supports
Validation rules:
listenmust not be empty.certandkeymust be configured together.require_client_cert: truerequiresclient_ca.basic.usernameandbasic.passwordmust both be non-empty.
plugins
Each plugin definition uses the same outer structure:
- tag: cache_main
type: cache
args:
size: 4096
General rules:
tag- Unique plugin instance identifier.
- Must not be empty.
- Must be unique across the whole config.
type- Plugin type name.
- Must match a registered plugin factory.
args- Plugin parameters.
- Different plugins accept different shapes: object, string, array, or null.
Responsibilities of the Four Plugin Categories
server
Purpose: Accept DNS requests and send them into an executor entry.
Traits:
- Does not implement complex policy logic.
- Usually configures a bind address, TLS parameters, and an entry executor.
executor
Purpose: Perform actions.
Typical actions include:
- Query upstreams
- Generate local answers
- Read and write cache
- Adjust TTL
- Handle ECS
- Run fallback and concurrent races
- Perform observability and system integrations
matcher
Purpose: Evaluate conditions for use in sequence rules.
Typical match dimensions include:
- Query name
- Query type
- Client IP
- Response IP
- Response code
- Environment variables
- Sampling outcome
- Rate-limit state
provider
Purpose: Provide reusable datasets for matchers or other plugins.
Current main provider types:
domain_setip_setgeoipgeositeadguard_rule
The sequence Orchestration Model
sequence is the policy hub of ForgeDNS. Most non-trivial configs use it as the primary entry.
Example:
- tag: seq_main
type: sequence
args:
- matches:
- "$lan_clients"
- "qtype 1"
exec: "$cache_main"
- matches: "!$has_resp"
exec: "$forward_main"
- exec: "accept"
Each rule has two key fields:
matches- One matcher expression or an array of expressions.
- When it is an array, every condition must be true for the rule to match.
exec- The action to execute when the rule matches.
Referencing Plugins and Quick Setup
Reference Existing Plugins
Use $tag to reference a plugin that has already been defined:
- exec: "$forward_main"
- matches:
- "$is_internal"
- "!$has_resp"
exec: "$cache_main"
Quick Setup
If a sequence rule uses type + arguments instead of $tag, ForgeDNS creates a temporary plugin on the fly.
Example:
- exec: "forward 1.1.1.1 8.8.8.8"
- matches: "qname domain:example.com"
exec: "ttl 300"
Common quick setup forms today:
- matcher
_true_falseqname ...qtype ...qclass ...client_ip ...resp_ip ...ptr_ip ...cname ...mark ...env ...random ...rate_limiter ...rcode ...has_resphas_wanted_ansstring_exp ...
- executor
forward ...ttl ...sleep ...debug_print ...query_summary ...metrics_collector ...black_hole ...drop_respecs_handler ...forward_edns0opt ...ipset ...nftset ...
Built-In sequence Control Flow
Besides calling plugins, sequence.args[].exec can also use built-in control flow:
accept
- Ends the current
sequenceimmediately. - This is an explicit early stop, so callers do not continue with later rules.
- Does not build a response by itself.
- Typical use:
- Close out the pipeline after
cache,hosts, orarbitraryhas already written a response. - Stop later
forwardor side-effect stages once a branch has already made the decision.
- Close out the pipeline after
return
- Ends the current
sequenceimmediately and returns control to the caller. - Does not build a response.
- If the current
sequencewas entered viajump, the caller resumes at the rule afterjump. - If the current
sequenceis the top-level entry, this acts like an early exit from the current rule chain.
reject [rcode]
- Builds a DNS response from the current request immediately and ends the current
sequence. - The default
rcodeisREFUSED, so plainrejectmeans “reject this request”. - You can provide a decimal numeric code explicitly, for example:
reject 2=>SERVFAILreject 3=>NXDOMAIN
- The parameter currently accepts decimal integers only, not mnemonic names such as
SERVFAIL. - Callers do not continue with later rules.
mark ...
- Inserts one or more unsigned integer marks into
DnsContext.marks. - Supported forms:
mark 1mark 1 2 3mark 1,2,3
- Continues to the next rule in the current
sequence. - Does not build a response and does not terminate the current
sequence.
jump seq_tag
- Calls another
sequence; conceptually this behaves like a subroutine call. - The parameter must be the target
sequencetag without a leading$. - If the called
sequence:- reaches its tail normally, the current
sequenceresumes at the rule afterjump. - executes
return, the currentsequencealso resumes at the rule afterjump. - executes
accept,reject, or another operation that returnsStop, the currentsequencestops as well.
- reaches its tail normally, the current
goto seq_tag
- Transfers control to another
sequence; conceptually this behaves like a one-way jump. - The parameter must be the target
sequencetag without a leading$. - The current
sequencenever resumes aftergoto:- If the target
sequencereaches its tail, control does not return to the rules aftergoto. - If the target
sequenceexecutesreturn, thatreturnis propagated outward and still does not return to the rules aftergoto. - If the target
sequenceexecutesaccept,reject, or anotherStop, that result propagates outward directly.
- If the target
- This is useful when ownership of the request should be handed off permanently to another policy branch.
Example:
- matches: "$rate_ok"
exec: "mark 100"
- matches: "!$rate_ok"
exec: "reject 2"
Example showing the difference between jump and goto:
- tag: child_seq
type: sequence
args:
- exec: "mark 2"
- exec: "return"
- tag: parent_jump
type: sequence
args:
- exec: "mark 1"
- exec: "jump child_seq"
- exec: "mark 3"
- tag: parent_goto
type: sequence
args:
- exec: "mark 1"
- exec: "goto child_seq"
- exec: "mark 3"
parent_jumpends with marks1,2,3because execution resumes afterjump.parent_gotoends with marks1,2because execution never returns aftergoto.
Common Rule Syntax
Domain Rules
These forms appear in plugins such as qname, cname, domain_set, hosts, and redirect:
full:example.com- Exact match.
domain:example.com- Suffix match.
keyword:cdn- Substring match.
regexp:^api[0-9]+\\.example\\.com$- Regular-expression match.
example.com- Without a prefix, this usually behaves like
domain:example.com.
- Without a prefix, this usually behaves like
IP Rules
These forms appear in client_ip, resp_ip, ptr_ip, ip_set, and related plugins:
- Single IP:
1.1.1.1 - CIDR:
192.168.0.0/16 - IPv6 CIDR:
2400:3200::/32
Provider References
Matchers and providers can reference providers through:
$tag- References a defined provider with the required match capability.
- Domain-oriented references can target
domain_setorgeosite. - IP-oriented references can target
ip_setorgeoip.
&/path/to/file- Loads rules directly from a file.
Example:
args:
- "domain:example.com"
- "$core_domains"
- "&/etc/forgedns/domains.txt"
Unified Upstream Structure
forward.upstreams uses a shared UpstreamConfig shape.
Example:
upstreams:
- addr: "udp://1.1.1.1:53"
- addr: "https://resolver.example/dns-query"
bootstrap: "8.8.8.8:53"
timeout: 5s
enable_http3: true
Common fields:
addr- Upstream address.
- Defaults to UDP when no scheme is given.
- Supports
udp://,tcp://,tcp+pipeline://,tls://,tls+pipeline://,quic://,doq://,https://,doh://, andh3://. - DoH should include the full path, for example
https://resolver.example/dns-query.
dial_addr- Actual connection IP, while keeping the hostname from
addrfor SNI and certificate validation.
- Actual connection IP, while keeping the hostname from
port- Overrides the port.
bootstrap- Bootstrap DNS used to resolve the upstream hostname when
addris domain-based.
- Bootstrap DNS used to resolve the upstream hostname when
bootstrap_version4or6.
socks5- SOCKS5 proxy.
- Supports
host:portanduser:pass@host:port. - IPv6 must use
[addr]:port.
idle_timeout- Idle connection timeout in seconds.
max_conns- Maximum pool size.
insecure_skip_verify- Skips TLS certificate validation. Recommended only for test environments.
timeout- Per-query timeout. Default:
5s.
- Per-query timeout. Default:
enable_pipeline- Enables TCP or DoT pipelining.
enable_http3- Uses HTTP/3 for DoH.
so_mark- Linux
SO_MARK.
- Linux
bind_to_device- Linux
SO_BINDTODEVICE.
- Linux