bindantic
bindantic is a library for managing BIND9 DNS server configuration via Pydantic models.
Instead of manually editing named.conf, you describe the configuration in Python, and the
library generates correct BIND9 syntax and (optionally) places files into the required
directories.
Table of contents
Features
- Full support for all
named.confblocks -acl,controls,dnssec-policy,http,key,key-store,logging,options,remote-servers,server,statistics-channels,tls,trust-anchors,view,zone. - All common resource record types -
A,AAAA,CAA,CERT,CNAME,DNAME,DNSKEY,DS,HINFO,LOC,MX,NAPTR,NSEC,NS,PTR,RP,RRSIG,SOA,SPF,SRV,SSHFP,TLSA,TXT. - Built-in validation - pass strings, numbers, IP addresses, durations, and bindantic formats them correctly for BIND.
- Validated against a real BIND9, not just unit tests on strings - CI generates configuration
for every block and resource record type and runs it through a real
named-checkconf, so the output isn't just "what we believe BIND9 syntax looks like." - Syntax generation in one line -
model.model_bind_syntax()for any block or the wholenamed.conf,zone.model_bind_syntax_zone_file()for a ready-to-use zone file. - Generate files without writing, or write straight to disk -
config.generate_files()returns a list of generated files;config.write_files("./my_config")createsnamed.conf, zones, keys, and DNSSEC policies, organised into subdirectories. - Python 3.10+, static typing (
py.typed), 97%+ test coverage. - No extra dependencies - only Pydantic.
Installation
pip install bindantic
[!WARNING]
named-checkconfversion: bindantic generates syntax according to the latest stable BIND 9.20.x release. Thenamed-checkconfutility from your distro'sbind9-utils/bind-utilspackage may be several minor versions older than that and reject directives it doesn't recognize yet (this project's own CI hit exactly that with an outdated apt package - see CHANGELOG.md). Always check generated configuration with the samenamed-checkconfversion as your production server, if possible.
Quick start
Example of a minimal configuration:
from bindantic import (
ARecord,
NamedConfig,
NSRecord,
OptionsBlock,
SOARecord,
ZoneBlock,
ZoneTypeEnum,
)
config = NamedConfig(
options_block=OptionsBlock(
directory="/etc/bind",
recursion=True,
allow_recursion=["localhost", "localnets"],
listen_on=["any"],
listen_on_v6=["any"],
),
zone_blocks=[
ZoneBlock(
comment="optional comment",
name="example.com",
zone_type=ZoneTypeEnum.PRIMARY,
file="zones/example.com.zone",
resource_records=[
SOARecord(
mname="ns1.example.com",
rname="admin.example.com",
serial=2026010101,
refresh=10800,
retry=3600,
expire=604800,
minimum=3600,
origin="example.com",
ttl=3600,
),
NSRecord(nsdname="ns1.example.com", comment="optional comment"),
ARecord(name="@", address="192.168.1.1"),
],
)
],
)
Output of config.model_bind_syntax()
options {
allow-recursion {
localhost;
localnets;
};
directory "/etc/bind";
listen-on {
any;
};
listen-on-v6 {
any;
};
recursion yes;
};
# optional comment
zone example.com. {
type primary;
file "zones/example.com.zone";
};
Output of config.zone_blocks[0].model_bind_syntax_zone_file()
$TTL 3600
$ORIGIN example.com.
@ IN SOA ns1.example.com. admin.example.com. (
2026010101 ; Serial number (YYYYMMDDNN)
10800 ; Refresh time
3600 ; Retry time
604800 ; Expire time
3600 ; Minimum TTL
)
@ IN NS ns1.example.com. ; optional comment
@ IN A 192.168.1.1
Output of config.generate_files()
[
GeneratedFile(
path=PosixPath("/etc/bind/zones/example.com.zone"),
content="<CONTENT>",
type="zone",
),
GeneratedFile(
path=PosixPath("/etc/bind/named.conf"),
content="<CONTENT>",
type="config",
),
]
Output of config.write_files(base_dir="./my_config")
my_config/
├── named.conf
└── zones/
└── example.com.zone
named.conf (directory is rewritten to the base_dir you actually pass in):
# Automatically generated by bindantic - please adjust!
options {
allow-recursion {
localhost;
localnets;
};
directory "my_config";
listen-on {
any;
};
listen-on-v6 {
any;
};
recursion yes;
};
# optional comment
zone example.com. {
type primary;
file "zones/example.com.zone";
};
zones/example.com.zone is identical to the model_bind_syntax_zone_file() output above.
More examples
Focused, runnable scripts for common real-world setups - see also Examples in the documentation:
examples/multi_view_split_horizon.py- split-horizon DNS: internal clients see private records, everyone else sees public ones.examples/secondary_zone_tsig.py- a primary/secondary zone pair with TSIG-authenticated zone transfers.examples/dnssec_signed_zone.py- a DNSSEC-signed zone, end to end: key-store,dnssec-policy(KSK+ZSK), and a zone using that policy.examples/logging_and_statistics.py- structured logging plus a statistics channel for monitoring tools to scrape.examples/controls_and_rndc.py- an explicitrndccontrol channel secured with its own key.examples/dns_over_tls_forwarding.py- forwarding to upstream resolvers over DNS-over-TLS.examples/remote_servers_and_trust_anchors.py- a reusableremote-serverslist plus DNSSEC trust anchors.examples/response_policy_zone.py- blocking/redirecting domains with a Response Policy Zone, plus a catalog zone.
Documentation
Full documentation, including an exhaustive per-field API reference generated from the models themselves, is at DVSAWR.github.io/bindantic.
Contributing
Contributions are welcome - see CONTRIBUTING.md for the development setup and workflow. Please review the Code of Conduct before participating, and see SECURITY.md to report a security issue privately instead of opening a public one.
Versioning
bindantic follows Semantic Versioning.
- Public API - everything importable from the top-level
bindanticpackage (models, enums, field type aliases) is covered by semver guarantees. - Internal - any module prefixed with
_(e.g.bindantic._base_model,bindantic._base_types_validation) is an implementation detail and may change without notice. - Major - removing/renaming a public model or field, or a change that makes previously valid input invalid, or a change to the generated BIND syntax output.
- Minor - new models, new optional fields, support for new BIND directives.
- Patch - bug fixes that don't change the public API surface.
bindantic targets the latest stable BIND 9.20.x release; tracking a new BIND directive is treated as a minor bump unless it conflicts with existing behavior.
See CHANGELOG.md for the release history.
License
MIT - see LICENSE.
Release files for bindantic 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bindantic-2.0.0.tar.gz | 60.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bindantic-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 128.0 kB
Release files / bindantic-2.0.0.tar.gz
| Download URL | bindantic-2.0.0.tar.gz |
|---|---|
| Size | 60.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ef63613fe8a805c8be62f08bfda9e1ecaac8838490354202d05dee965ea932c5
|
|
BLAKE2b-256 checksum How to use checksums |
8f7bda6aef6e005e9808c9f7e1252e8bec1f70caebb1552b707d172ff177bd74
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.
Transparency logRelease files / bindantic-2.0.0-py3-none-any.whl
| Download URL | bindantic-2.0.0-py3-none-any.whl |
|---|---|
| Size | 67.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c8b25bc10440bbac8c8ca445dfde5604357c216ce7411b5140be31771eeae387
|
|
BLAKE2b-256 checksum How to use checksums |
644be1a1909cd8365936deffc571376242e39f9593d8e9ae3a4f3e96f1132c68
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.
Transparency log