Valkyrian Labs logo

Quick start

Set up forti-hole, see what your lists compile to and whether they fit the device, sync, verify, and attach a DNS filter profile to a firewall policy.

Quick start

From a FortiGate with a forti profile to blocklists the device verifiably holds. Nothing is written to the FortiGate until step 4.

  1. Write the configuration

    1forti-hole setup

    setup checks the device and writes ~/.forti/hole/config.json with the default lists, three profiles and "enabled": false. --profile NAME picks a forti profile other than the default one; --offline skips the device check.

  2. See what the lists compile to

    1forti-hole compile --device

    compile downloads every list, deduplicates them and reports, per source, how many entries were valid, invalid, duplicated within the list and already contributed by another list, then the totals: unique names, FortiOS feed lines and the feeds they go into. --device also reads the FortiGate's entry limit and memory and says whether the set fits:

    1Variant choice2  hagezi-tif medium: 2,221,664 lines, does not fit: 2221664 lines needed, 900000 available ...3  hagezi-tif mini: 807,914 lines, fits  <- chosen4 5Budget6  entries: 807,914 of 900,000 available (1,000,000 limit reported by the device, 0 in other feeds, 10% headroom) -> fits7  memory:  54.7% used now, 57.8% predicted (+63 MB at 175 bytes/line), ceiling 85.0% -> fits

    --write DIR writes every feed as a text file and the report as JSON, for inspection.

  3. Review the plan

    1forti-hole plan

    Every feed and DNS filter profile a sync would create or change, with the reason and the number of entries sent. plan writes nothing to the FortiGate.

  4. Enable and sync

    Set "enabled": true in ~/.forti/hole/config.json, then:

    1forti-hole sync

    The first sync creates the feeds (fh-all-1, fh-light-1, ...) and the DNS filter profiles (fh-light, fh-standard, fh-strict), waits for FortiOS to load the new feeds, pushes them, and verifies each on the device. Later syncs send only what changed upstream.

  5. Check what the device holds

    1forti-hole status --verify --full

    Reads every entry back from the device and compares it with what was compiled. Without --full, --verify samples pages; without --verify, status compares counts and update times.

  6. Attach a profile

    The feeds filter nothing until a DNS filter profile that blocks them is on a firewall policy that DNS queries cross (Where filtering applies):

    1forti-hole attach standard --policy 2

    This sets the policy's DNS filter to fh-standard (and turns its security profiles on if they were off). forti-hole detach --policy 2 puts back what was there before.

  7. Keep it current

    1systemctl --user enable --now forti-hole.timer     # Linux2brew services start forti-hole                     # Homebrew

    Twice a day; see Scheduling.

If something looks wrong

forti-hole doctor checks the firmware, the entry limit, memory, free categories, name collisions, whether any policy uses a forti-hole profile, and whether the last sync finished. Troubleshooting explains every exit status.