Configuration
~/.forti/hole/config.json ($FORTI_HOME/hole/config.json). Unknown keys are refused, with the key
and its location in the message. forti-hole setup writes the example below
(share/forti-hole/config.example.json).
On this page
Configuration
Top level
sources[]
budget
feeds, sync, fetch, dnsfilter
The example
Files
Top level
Key Default Meaning format1Configuration format enabledfalseNothing is written to the FortiGate until true profile""The forti profile; empty: the default profile vdom""A VDOM; empty: the profile's prefix"fh-"Name prefix of every object forti-hole owns (2–20 characters, ending in -) tiersTier names, in order sourcesThe lists (Lists, tiers and profiles ) profiles{"name": {"tiers": [...]}}; each is the DNS filter profile <prefix><name>allow, allow_regex, block[]Exceptions and extra entries encoding"strict"strict or compact (Capacity )reference{"url", "format": "tranco"}: real names for compact
sources[]
Key Meaning id1–48 characters of [a-z0-9._-], unique tierOne of tiers formatadblock, hosts, domains, wildcard, subtree-domainsurlhttps://… or file:///…variants, variant, autoInstead of url: named URLs, the one to use (auto or a name), and the auto order enabledtrue
budget
Key Default Meaning entry_headroom_percent10Kept free of the device's domain-entry limit memory_headroom_percent15Kept free of device memory after the change fallback_limit1000000The entry limit when the device does not report one bytes_per_linelearned Device memory per feed line; leave unset to use the measured value on_overrun"refuse"refuse (nothing written, exit 9) or warn
feeds, sync, fetch, dnsfilter
Key Default Meaning feeds.max_entries131072Lines per device feed feeds.category_min, category_max192, 221The remote categories forti-hole may use sync.max_shrink_percent20A list this much smaller than its last good copy is rejected sync.chunk_entries50000Entries per delta request sync.load_timeout_seconds120Waiting for FortiOS to load a feed or apply a push sync.verify_samples, verify_page3, 500Pages read back per changed feed, and their size fetch.timeout_seconds120Per download fetch.max_bytes128 MiB Largest list accepted fetch.parallel6Simultaneous downloads fetch.ca_bundleExtra trusted CAs (PEM); verification stays on dnsfilter.manage_profilestrueCreate and maintain the DNS filter profiles dnsfilter.block_action"block"block (NXDOMAIN), redirect (block portal) or block-sevrfaildnsfilter.error_allowtrueftgd-dns options error-allow: resolve normally when FortiGuard rating is unavailablednsfilter.logtrueLog blocked queries
The example
1 { 2 "format" : 1 , 3 "enabled" : false , 4 "profile" : "" , 5 "prefix" : "fh-" , 6 "tiers" : [ "base" , "plus" , "ultimate" , "threat" ], 7 "sources" : [ 8 { "id" : "stevenblack" , "tier" : "base" , "format" : "hosts" , "url" : "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" }, 9 { "id" : "hagezi-pro" , "tier" : "base" , "format" : "adblock" , "url" : "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/pro.txt" }, 10 { "id" : "hagezi-pro-plus" , "tier" : "plus" , "format" : "adblock" , "url" : "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/pro.plus.txt" }, 11 { "id" : "hagezi-ultimate" , "tier" : "ultimate" , "format" : "adblock" , "url" : "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/ultimate.txt" }, 12 { "id" : "hagezi-tif" , "tier" : "threat" , "format" : "adblock" , 13 "variants" : { "full" : "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/tif.txt" , 14 "medium" : "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/tif.medium.txt" , 15 "mini" : "https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/tif.mini.txt" }, 16 "variant" : "auto" , "auto" : [ "medium" , "mini" ]} 17 ], 18 "profiles" : { 19 "light" : { "tiers" : [ "base" , "threat" ]}, 20 "standard" : { "tiers" : [ "base" , "plus" , "threat" ]}, 21 "strict" : { "tiers" : [ "base" , "plus" , "ultimate" , "threat" ]} 22 }, 23 "encoding" : "strict" , 24 "budget" : { "entry_headroom_percent" : 10 , "memory_headroom_percent" : 15 , "on_overrun" : "refuse" }, 25 "dnsfilter" : { "manage_profiles" : true , "block_action" : "block" , "error_allow" : true , "log" : true } 26 }
Files
Path (under ~/.forti/hole/) What config.jsonYours state.jsonLast confirmed device state per feed, categories, list cache metadata, memory measurements feeds/<feed>.txtExactly what each feed was last confirmed to hold cache/<list>.listThe last good copy of every list journal.jsonPresent only while a sync writes, or after one stopped part-way history.jsonlOne line per sync
Every file is 0600 in a 0700 directory and written atomically. Credentials stay in forti's secret
store. Plans and the audit log are the engine's (~/.forti/state/).