SkillAgentSearch skills...

Foghorn

Fast, ultra-customizeable, pluggable DNS server (forwarding, recursive, or none) with caching, filtering, routing, DNSSEC, admin UI, and plugins. UDP/TCP/DoT/DoH.

Install / Use

/learn @zallison/Foghorn

README

Foghorn Configuration & Operations

Foghorn is a versatile DNS server designed for flexibility and performance. Built on a robust caching forwarder or recursive resolver foundation, it seamlessly integrates with YAML-based configuration and modular plugins to transform into a powerful ad-blocker, local hosts service, kid-safe filter. Fine-tune its capabilities with a Redis backend, InfluxDB logging, and customizable function-cache sizes tailored to your specific needs.

With built-in admin and API server support, Foghorn empowers you to monitor and manage its operations efficiently. Plugins extend its functionality by providing their own status pages, seamlessly integrated into the admin dashboard. Newer releases add DNSSEC signing helpers, zone transfers (AXFR/IXFR), RFC 8914 Extended DNS Errors (EDE), and SSH host key utilities so you can treat DNS as a first-class security and operations tool.

Python Tests Test Coverage Docker Pulls PyPI Downloads BuyMeACoffee

<img src="https://raw.githubusercontent.com/zallison/foghorn/refs/heads/main/assets/screenshot-1.png" width=300px />

The configuration file is validated against a JSON Schema, but you rarely need to read the schema directly. This guide walks through the main sections (vars, server, upstreams, logging, stats, and plugins), then shows concrete examples for every built‑in plugin.

Table of Contents

Additonal Documentation


0. Thanks

With special thanks to Fiona Weatherwax for their contributions and inspiration, to the dnslib team for the low level / on wire primitives, and to dnspython for the DNSSEC implementation. Additional shout outs to the whole python community, and the teams of fastapi, pydantic, black, ruff, pytest, and every other giant on whose shoulders I stand.

Also thanks to my junior developers, AI from both local and remote models, some via warp.dev, who keeps my docstrings and unit tests up to date, creates good commit messages, and other janitorial tasks. Also ~~a lot of help with the~~ all the HTML/JS. Because I'm just not good at it.

1. Quick Start

1.1 Install

Foghorn can be installed a few different ways, depending on how you prefer to run services:

• From PyPI (recommended for most users)

  Install the latest released version into your Python environment:
  pip install foghorn
  This gives you the foghorn CLI and library directly on your host system.

• From source (GitHub) If you want to track development, hack on plugins, or run a specific commit/branch, clone the repository and install it in editable mode:

git clone https://github.com/zallison/foghorn.git
cd foghorn
pip install -e .

This keeps your local checkout and installed code in sync as you make changes.

• Prebuilt Docker images (amd64 and armhf) If you prefer to run Foghorn in a container, prebuilt images for both amd64 and armhf are available on Docker Hub at https://hub.docker.com/r/zallison/foghorn. Pull the image for your architecture and run it with your configuration mounted as /foghorn/config.yaml, along with any port mappings you need for DNS, DoT/DoH, and the admin web UI.

 docker run --name foghorn -v ./config/:/foghorn/config/ -p 53:53/udp -p 53:53/tcp -p 5380:5380 --privileged zallison/foghorn:latest

1.2 Quick config

This example listens on all interfaces for UDP/TCP DNS and forwards to a public DoT resolver. It also enables a simple in-memory cache.

# config/config.yaml

vars:
  ENV: prod

server:
  listen:
	dns:
	  udp:
		enabled: true
		host: 0.0.0.0
		port: 53
	  tcp:
		enabled: true
		host: 0.0.0.0
		port: 53
  cache:
	module: memory  # memory | sqlite | redis | memcached | mongodb | none

upstreams:
  strategy: failover        # failover | round_robin | random
  max_concurrent: 1         # 1 | 2 | 4 ...
  endpoints:
	- host: 1.1.1.1
	  port: 853
	  transport: dot        # udp | tcp | dot
	  tls:
		server_name: cloudflare-dns.com

plugins: []

You can start Foghorn with:

foghorn --config config/config.yaml

From here you layer in plugins to get adblocking, hosts files, per-user allowlists, and more.

Makefile helpers

For local development there is a Makefile with a few convenience targets:

  • make run – create a venv if needed and start Foghorn with config/config.yaml.
  • make env / make env-dev – create the virtualenv in ./venv and install dependencies (with dev extras for env-dev).
  • make build – prepare the development environment (keeps the JSON schema up to date).
  • make schema – regenerate assets/config-schema.json from the Python code.
  • make test – run the test suite with coverage.
  • make dnssec-sign-zone – sign a BIND-style zone file with DNSSEC using the bundled helper script, writing a signed zone that can be served by the ZoneRecords plugin.
  • make clean – remove the venv, build artefacts, and temporary files.
  • make docker, make docker-build, make docker-run, make docker-logs, make docker-clean, make docker-ship – build and run Docker images/containers.
  • make package-build / make package-publish / make package-publish-dev – build and (optionally) publish Python packages.
  • make ssl-cert – generate a self-signed TLS key and certificate under ./var using openssl req -x509.

Additional Documentation


2. Configuration layout overview

2.1 Top-level keys

At the top level the schema defines these keys:

  • vars: key/value variables for interpolation inside the rest of the file.
  • server: listener, DNSSEC, resolver, cache, and admin HTTP settings.
  • upstreams: how outbound DNS queries are sent.
  • logging: global logging level and outputs.
  • stats: runtime statistics and query-log persistence.
  • plugins: the ordered list of plugins that wrap each query.

Conceptually, a request flows like this:

client ---> UDP/TCP/DoH listener
		---> DNS cache (memory, redis, etc) (optional)
		---> plugins (pre_resolve chain)
		---> [maybe upstream DNS calls or recursive resolving]
		---> plugins (post_resolve chain)
		---> response or deny

Note: when a pre_resolve plugin returns an override decision the generated respon

Related Skills

View on GitHub
GitHub Stars38
CategoryDevelopment
Updated17d ago
Forks1

Languages

Python

Security Score

95/100

Audited on Mar 24, 2026

No findings