bindantic
bindantic - 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.
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 – the library will format them correctly for BIND.
- 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 / write to disk
config.generate_files()- returns a list of generated filesconfig.write_files("./my_config")- createsnamed.conf, zones, keys, DNSSEC policies and organises them into subdirectories.
- Python - 3.10+, static typing, 96% test coverage.
- No extra dependencies - only Pydantic
Installation
pip install bindantic
⚠️ NOTE:
The named-checkconf utility from the bind-utils package may be older than your BIND server and may not recognise new directives.
bindantic generates syntax according to the latest stable BIND 9.20.x version. If you check the configuration with an older utility you may get errors. Always use the same version of named-checkconf as your 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="./examples/example")`
example_bind/
├── named.conf
└── zones/
└── example.com.zone
# Automatically generated by bindantic - please adjust!
options {
allow-recursion {
localhost;
localnets;
};
directory "examples/example";
listen-on {
any;
};
listen-on-v6 {
any;
};
recursion yes;
};
# optional comment
zone example.com. {
type primary;
file "zones/example.com.zone";
};
$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
The file ./examples/manual_example.py contains usage examples for all supported models.
More examples
Focused, runnable scripts for common real-world setups:
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.
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.
Release files for bindantic 1.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-1.0.0.tar.gz | 56.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bindantic-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 120.1 kB
Release files / bindantic-1.0.0.tar.gz
| Download URL | bindantic-1.0.0.tar.gz |
|---|---|
| Size | 56.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
050329692c95cada17c4cb578994ff7e762553c1f05232d727002b974d347f33
|
|
BLAKE2b-256 checksum How to use checksums |
e890ca43c03e8eb22cb53a633312b4efb19eecb593f3b78fd5991dc1a8c62a5c
|
| 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-1.0.0-py3-none-any.whl
| Download URL | bindantic-1.0.0-py3-none-any.whl |
|---|---|
| Size | 63.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8d269bd6309f5080b5d1b96f49593bf6270a28dc074c22bae7b729d5dc37656c
|
|
BLAKE2b-256 checksum How to use checksums |
24d8ba9954c0a616e6feb56054d96f9dc0163297f38d0d9d8f89eb2a3950b6e0
|
| 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