Full Reference Config
This page shows one commented JSON example with every supported configuration section and every supported option.
Comments are supported in real keen-pbr config files, so you can use this example directly as a starting point.
List names, outbound tags, and DNS server tags must match
^[a-z][a-z0-9_]*$ and must be at most 24 characters.config.json
{
// Optional device label shown in the browser title and beneath the keen-pbr logo.
// Maximum length: 128 characters. Default: empty (the standard keen-pbr
// branding is used).
"device_name": "Home router",
// Global daemon settings.
// All fields in this section are optional.
"daemon": {
// Path to the PID file.
// Default: no PID file is written when omitted.
"pid_file": "/var/run/keen-pbr.pid",
// Directory used for remote-list cache files and cache metadata.
// Default: "/var/cache/keen-pbr".
"cache_dir": "/var/cache/keen-pbr",
// Firewall backend selection.
// Supported values: "auto", "iptables", "nftables".
// Default: "auto".
"firewall_backend": "auto",
// Skip packets that already have an fwmark before keen-pbr touches them.
// Default: true (also when set to null).
"skip_marked_packets": true,
// Clear dnsmasq-managed dynamic sets during a full apply or runtime restart.
// Default: true (also when set to null).
"clear_dynamic_sets_on_apply": true,
// Optional initial hash table size for ipsets created by the iptables backend.
// Has no effect with nftables. Minimum: 1; maximum: 2147483648. Omit or set
// to null to use the ipset default (1024).
// Changing it while iptables is running recreates owned ipsets and clears
// dnsmasq-learned entries.
"ipset_hashsize": null,
// Optional maximum element count for ipsets created by the iptables backend.
// Has no effect with nftables. Minimum: 1; maximum: 4294967295. Omit or set
// to null to use the ipset default (65536).
// Changing it while iptables is running recreates owned ipsets and clears
// dnsmasq-learned entries.
"ipset_maxelem": null,
// Reuse the currently live list sets during safe runtime refreshes.
// Default: true; failed preflight falls back to PreserveSets.
"reuse_static_sets_on_runtime_refresh": true,
// Install IPv4/IPv6 firewall sets and resolver targets.
// Default: true (also when set to null). If the system has no IPv6 support,
// keen-pbr logs an error and continues in IPv4-only mode.
"ipv6_enabled": true,
// Default strict routing behavior for interface outbounds.
// Default: false.
"strict_enforcement": false,
// Terminal action used by strict enforcement.
// Supported values: "unreachable" (return a network error) or "blackhole"
// (silently drop packets). Default: "unreachable".
"strict_enforcement_action": "unreachable",
// Maximum allowed size for downloaded remote content such as URL-backed lists.
// Default: 8388608 bytes (8 MiB).
"max_file_size_bytes": 8388608,
// Maximum stdout bytes captured per firewall verification command.
// Use 0 for unlimited capture.
// Default: 262144.
"firewall_verify_max_bytes": 262144,
// Maximum time in seconds allowed for privileged helpers and hooks.
// Minimum: 1. Default: 30.
"exec_timeout_seconds": 30,
// Maximum time in seconds to wait for resolver configuration generation
// after the resolver reload hook completes. Minimum: 1. Default: 120.
"resolver_ready_timeout_seconds": 120,
// Grace period in seconds after SIGTERM before a timed-out helper receives
// SIGKILL. Minimum: 0. Default: 2.
"exec_kill_grace_seconds": 2
},
// Embedded HTTP API and Web UI settings.
// WARNING: This section is NOT available in keen-pbr-headless package
"api": {
// Enable or disable the HTTP API / Web UI.
// Default: false.
"enabled": true,
// Listen address for the API server.
// Default: "0.0.0.0:12121".
"listen": "0.0.0.0:12121",
// Maximum request body size in bytes. Minimum: 1024. Default: 1048576.
"max_request_body_bytes": 1048576,
// Maximum time in seconds spent reading an HTTP request.
// Minimum: 1. Default: 15.
"read_timeout_seconds": 15,
// Maximum time in seconds spent writing an HTTP response.
// Minimum: 1. Default: 15.
"write_timeout_seconds": 15,
// Idle timeout in seconds for HTTP keep-alive connections.
// Minimum: 1. Default: 20.
"keep_alive_timeout_seconds": 20,
// Optional HTTP API authentication settings. Default: disabled.
"authentication": {
"enabled": false
},
// Exact HTTP or HTTPS origins allowed to call the authenticated API.
// Omit this object when no CORS origins are needed.
"cors": {
"allowed_origins": ["https://router.example"]
}
},
// All supported outbound types.
// Tags are referenced by route rules, DNS server detours, and list detours.
// "type" is required and supports: interface, table, blackhole, ignore,
// urltest, and icmptest.
"outbounds": [
{
// "interface" sends traffic through a specific network interface.
"type": "interface",
// Unique outbound tag.
// Default: no default, required.
"tag": "vpn",
// Egress interface name.
// Default: no default, required for type="interface".
"interface": "wg0",
// Optional IPv4 gateway for the interface outbound.
// Default: null
"gateway": "10.8.0.1",
// Optional IPv6 gateway for the interface outbound.
// Default: null
"gateway6": "2001:db8::1",
// Per-outbound strict-enforcement override.
// Overrides daemon.strict_enforcement when set.
// Default: inherit daemon.strict_enforcement.
"strict_enforcement": true,
// Per-outbound terminal action override for strict enforcement.
// Supported values: "unreachable" or "blackhole".
// Default: inherit daemon.strict_enforcement_action.
"strict_enforcement_action": "unreachable"
},
{
// Another interface outbound, often used for the normal WAN path.
"type": "interface",
"tag": "wan",
"interface": "eth0",
// Set a gateway only when it is stable. For a dynamic WAN gateway, use a
// table outbound with table=254 (the main routing table) instead.
"gateway": "172.12.33.1"
},
{
// "table" reuses an existing kernel routing table.
"type": "table",
// Unique outbound tag.
"tag": "wan_as_table",
// Existing Linux routing table ID.
// Default: no default, required for type="table".
"table": 254
},
{
// "blackhole" drops matching traffic.
"type": "blackhole",
// Unique outbound tag.
"tag": "block"
},
{
// "ignore" lets matching traffic bypass keen-pbr handling
// and continue with the system's normal routing.
"type": "ignore",
// Unique outbound tag.
"tag": "direct"
},
{
// "urltest" probes several candidate outbounds and selects one automatically.
"type": "urltest",
// Unique outbound tag.
"tag": "auto_select",
// Probe URL used for latency / reachability checks.
// Default: no default, required for type="urltest".
"url": "https://www.gstatic.com/generate_204",
// Probe interval in milliseconds.
// Default: 180000 for urltest; 60000 for icmptest.
"interval_ms": 180000,
// Timeout for each individual probe attempt in milliseconds.
// Default: 5000 for urltest; 1000 for icmptest.
// Omit this field or set it to null to use the default for the outbound type.
"probe_timeout_ms": 5000,
// Do not switch if the new candidate is only slightly better than the current one.
// Minimum: 0 ms. Default: 100 for urltest.
"tolerance_ms": 100,
// Compatibility field for older configs.
// Urltest always appends terminal IPv4/IPv6 unreachable routes as a kill-switch.
// This setting currently has no additional effect for urltest outbounds.
"strict_enforcement": true,
// Handling of established conntrack flows when a healthy child changes.
// Supported values: "preserve" (default) or "delete". Failed selected
// paths are always cleaned up.
"conntrack_on_switch": "preserve",
// Ordered outbound groups.
// Default: no default, required for type="urltest".
// Lower weight is preferred before higher weight.
"outbound_groups": [
{
// Relative priority of this group.
// Default: 1.
"weight": 1,
// Candidate outbound tags inside this group.
// Supported child types: interface, table, blackhole.
"outbounds": ["vpn", "wan_as_table"]
},
{
// If the first group is unhealthy, keen-pbr can fall back to this one.
"weight": 2,
"outbounds": ["wan", "block"]
}
],
// Probe retry behavior.
"retry": {
// Retry attempts before a probe is treated as failed.
// Default: 3.
"attempts": 3,
// Delay between retries in milliseconds.
// Default: 1000.
"interval_ms": 1000
},
// Circuit breaker settings for unstable outbounds.
"circuit_breaker": {
// Consecutive failures before the circuit opens.
// Minimum: 1; maximum: 20. Default: 5.
"failure_threshold": 5,
// Consecutive successes needed to close the circuit again.
// Minimum: 1; maximum: 20. Default: 2.
"success_threshold": 2,
// Cooldown before moving from open to half-open.
// Minimum: 1 ms; maximum: 86400000 ms. Default: 30000.
"timeout_ms": 30000,
// Max probe requests allowed while half-open.
// Minimum: 1; maximum: 10. Default: 1.
"half_open_max_requests": 1
}
},
{
// "icmptest" sends ICMP Echo probes through candidate outbounds and
// selects a healthy candidate automatically.
"type": "icmptest",
"tag": "auto_ping",
// Probe interval in milliseconds. Default: 60000.
"interval_ms": 60000,
// ICMP Echo requests per candidate run. Minimum: 1; maximum: 10.
// Default: 3.
"count": 3,
// Maximum failed packets allowed in a successful run. Minimum: 0.
// It must be less than count. Default: 0.
"max_failed": 0,
// Pause between sequential ICMP attempts. Minimum: 100 ms; maximum:
// 1000 ms. Default: 200.
"packet_interval_ms": 200,
// Timeout for each ICMP attempt. Runtime range: 100-5000 ms.
// Default: 1000.
"probe_timeout_ms": 1000,
// Replies slower than this are failures. Minimum: 1 ms and it must not
// exceed probe_timeout_ms. Default: 500.
"max_rtt_ms": 500,
// Do not switch when the new candidate is only slightly better.
// Minimum: 0 ms. Default: 10.
"tolerance_ms": 10,
// Preserve or delete established conntrack flows when a healthy child
// changes. Supported values: "preserve" (default) or "delete".
"conntrack_on_switch": "preserve",
// Ordered groups of explicit outbound/target pairs. Required for
// type="icmptest". Every candidate requires both fields below.
"outbound_groups": [
{
"weight": 1,
"candidates": [
{
// Interface or table outbound tag.
"outbound": "vpn",
// Literal IPv4 or IPv6 address to ping through that outbound.
"target": "1.1.1.1"
},
{
"outbound": "wan",
"target": "9.9.9.9"
}
]
}
]
}
],
// Lists may use one or more of these sources: url, domains, ip_cidrs, file.
"lists": {
"inline_domains": {
// Inline domains.
// Domains match the domain itself and its subdomains.
// Wildcards such as "*.example.com" are optional: "example.com" has the
// same effect.
"domains": ["example.com", "othersite.net"],
// Time in milliseconds that IPs resolved by dnsmasq for these domains
// remain in the dynamic set. 0 keeps them indefinitely.
// Set this higher than dnsmasq max-cache-ttl (which is in seconds), with
// a safety margin. For max-cache-ttl=300, use at least 2100000 (35 min).
// Default: 0 (no timeout); this example uses 24 hours.
"ttl_ms": 86400000
},
"inline_ips": {
// Inline IPv4/IPv6 addresses or CIDRs.
"ip_cidrs": [
"93.184.216.34",
"10.0.0.0/8",
"2001:db8::1",
"2001:db8::/32"
]
},
"remote_list": {
// Remote list URL.
"url": "https://raw.githubusercontent.com/v2fly/domain-list-community/refs/heads/master/data/apple",
// Optional outbound used to download this list.
// Supported detour targets are routable outbounds such as interface, table, or urltest.
// Default: null (use the system's normal routing)
"detour": "auto_select",
// Time in milliseconds that IPs resolved by dnsmasq for these domains
// remain in the dynamic set. 0 keeps them indefinitely.
// Set this higher than dnsmasq max-cache-ttl (which is in seconds), with
// a safety margin. For max-cache-ttl=300, use at least 2100000 (35 min).
// Default: 0 (no timeout); this example uses 24 hours.
"ttl_ms": 86400000
},
"local_file_list": {
// Local list file path.
"file": "/etc/keen-pbr/local.lst",
// Time in milliseconds that IPs resolved by dnsmasq for these domains
// remain in the dynamic set. 0 keeps them indefinitely.
// Set this higher than dnsmasq max-cache-ttl (which is in seconds), with
// a safety margin. For max-cache-ttl=300, use at least 2100000 (35 min).
// Default: 0 (no timeout); this example uses 24 hours.
"ttl_ms": 86400000
},
"mixed_sources": {
// keen-pbr currently supports combining multiple sources in one list.
// This is supported, but separate lists are usually easier to maintain.
"domains": ["intranet.example"],
"ip_cidrs": ["192.168.50.0/24"],
"file": "/etc/keen-pbr/mixed.lst",
"url": "https://example.com/mixed.lst",
"detour": "vpn",
// ttl_ms applies to domain sources in this list. See the inline_domains
// example above for sizing guidance.
"ttl_ms": 86400000
}
},
// DNS configuration.
// dns.system_resolver is required for daemon runtime.
"dns": {
// Resolver used for runtime integration and TXT health checks.
// Default: no default, required by the running daemon.
"system_resolver": {
"address": "127.0.0.1"
},
// Optional built-in DNS probe server for the Web UI and troubleshooting.
// To check that this computer sends DNS queries through keen-pbr, run:
// > nslookup check.keen.pbr
// The response should contain answer_ipv4 (127.0.0.88).
"dns_test_server": {
// IPv4 listen address in host:port form.
// Default: no default, required when dns_test_server is present.
"listen": "127.0.0.88:12153",
// IPv4 A-record answer returned by the probe server.
// Default: the host part of listen.
"answer_ipv4": "127.0.0.88"
},
// All supported DNS server styles.
"servers": [
{
// Plain static DNS server with no detour.
"tag": "google_dns",
// Supported values: "static" (default) or "keenetic".
// Default: "static".
"type": "static",
// Address for static DNS servers.
// Default: no default, required for type="static".
"address": "8.8.8.8"
},
{
// Static DNS server reached through an interface outbound.
"tag": "vpn_dns",
"address": "10.8.0.1:5353",
// Optional outbound used to contact this DNS server.
// Supported detour targets: interface, table, urltest.
// Not allowed: blackhole, ignore.
// Default: use the system's normal routing.
"detour": "vpn"
},
{
// Static DNS server reached through a urltest outbound.
"tag": "auto_dns",
"address": "[2606:4700:4700::1111]:53",
"detour": "auto_select"
},
{
// Reuse the router's built-in DNS settings through RCI.
// Configure DoT/DoH on the router first, then add this Keenetic DNS server.
// Available only on Keenetic and Netcraze routers.
"tag": "keenetic_dns",
"type": "keenetic"
}
],
// Domain-to-DNS-server rules.
"rules": [
{
// Whether this DNS rule is active.
// Default: true when omitted or set to null.
"enabled": true,
// Lists whose domains should be resolved by this server.
"list": ["inline_domains", "remote_list"],
// DNS server tag to use.
"server": "vpn_dns",
// Allow answers that resolve to private/local IP ranges.
// Default: false.
"allow_domain_rebinding": false
},
{
// Example rule for local services that intentionally resolve to RFC1918 addresses.
"list": ["mixed_sources"],
"server": "keenetic_dns",
"allow_domain_rebinding": true
},
{
// Example of a disabled DNS rule kept for later use.
"enabled": false,
"list": ["inline_domains"],
"server": "google_dns"
}
],
// Upstream DNS servers used when no DNS rule matches.
// Default: no upstream servers.
// WARNING: if you don't provide at least one DNS server here, your Internet connectivity may fail.
"fallback": ["google_dns", "auto_dns", "keenetic_dns"]
},
// Firewall mark allocation.
// This section is optional.
"fwmark": {
// First fwmark assigned to routable outbounds as a hex string.
// Default: "0x00010000".
"start": "0x00010000",
// Fwmark bitmask as a hex string.
// Must contain one or more consecutive F nibbles.
// Default: "0x00FF0000".
"mask": "0x00FF0000"
},
// Policy-routing table allocation.
// This section is optional.
"iproute": {
// First routing table ID used for auto-allocated outbound tables.
// Default: 150.
// Avoid reserved IDs such as 128 and 250-260.
"table_start": 150,
// First policy-routing rule priority to allocate.
// Default: null, which inherits table_start.
"rule_priority_start": null
},
// Route-processing rules.
"route": {
// Optional ingress interface filter.
// If omitted or empty, keen-pbr processes packets from any interface.
// Default: no filter.
"inbound_interfaces": ["br0", "wg-lan"],
"rules": [
{
// Basic list-based routing rule.
// Default for enabled: true when omitted or set to null.
"list": ["inline_domains", "remote_list"],
"outbound": "auto_select"
},
{
// Route inline IPs through an existing routing table.
"list": ["inline_ips"],
"outbound": "wan_as_table"
},
{
// Full filter example using DSCP, proto, source/destination address, and destination port.
"enabled": true,
// Match traffic only if dest domain/IP is in the list
"list": ["mixed_sources"],
// Match traffic only if protocol is TCP
// Possible values: null (for any protocol), tcp, udp, tcp/udp
"proto": "tcp",
// Match traffic only if DSCP tag equals this value.
// Supported: integer from 1 to 63.
"dscp": 46,
// Match traffic only if source IP matches
// Supported: single IP, CIDR
"src_addr": "192.168.10.0/24,192.168.20.0/24",
// Match traffic only if dest IP matches
// Supported: single IP, CIDR
"dest_addr": "203.0.113.0/24",
// Match traffic only if source port matches
// Supported: single port, multiple port comma-separated, range
"src_port": "1024-65535",
// Match traffic only if dest IP matches
// Supported: single port, multiple port comma-separated, range
"dest_port": "443,8443",
// Route all matched traffic to this outbound
"outbound": "vpn"
},
{
// Rules may omit "list" if another condition is present.
// This example blocks DNS to any destination except the trusted resolver subnet.
"proto": "udp",
"dest_port": "53",
"dest_addr": "!10.10.0.0/16",
"outbound": "block"
},
{
// Example "ignore" rule used as an exception before broader VPN rules.
"src_addr": "192.168.1.0/24",
"dest_addr": "192.168.0.0/16",
"outbound": "direct"
},
{
// IPv6 example: route HTTPS traffic to a documentation subnet via VPN.
"proto": "tcp",
"src_addr": "2001:db8:10::/64",
"dest_addr": "2001:db8:203::/48",
"dest_port": "443",
"outbound": "vpn"
},
{
// Example disabled rule kept for later use.
"enabled": false,
"list": ["inline_domains"],
"proto": "tcp/udp",
"dest_port": "!80,443",
"outbound": "wan"
}
]
},
// Automatic refresh for URL-backed lists.
// This section is optional.
"lists_autoupdate": {
// Enable or disable periodic refresh.
// Default: false.
"enabled": true,
// Standard 5-field cron expression.
// Required when enabled=true.
// No default value.
"cron": "0 4 * * 0"
}
}Notes
dns.servers[].detoursupportsinterface,table, andurltestoutbounds, but notblackholeorignore.lists[].detouris useful when a remote list should be downloaded through a VPN or other non-default path.route.rules[]must include at least one matching condition:list,dscp,src_port,dest_port,src_addr, ordest_addr.dns.rules[].allow_domain_rebindingis mainly for internal domains that intentionally resolve to private IP ranges.