Configuration
Basics
This document picks up where Getting Started and Records leave off, discussing details and less common scenarios.
YamlProvider
octodns.provider.yaml lays out the options for configuring the most commonly
used source of record data.
Dynamic Zone Config
In many cases octoDNS’s dynamic zone configuration is the best option for configuring octoDNS to manage your zones. In its simplest form that would look something like:
---
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
default_ttl: 3600
enforce_order: True
ns1:
class: octodns_ns1.Ns1Provider
api_key: env/NS1_API_KEY
route53:
class: octodns_route53.Route53Provider
access_key_id: env/AWS_ACCESS_KEY_ID
secret_access_key: env/AWS_SECRET_ACCESS_KEY
zones:
'*':
sources:
- config
targets:
- ns1
- route53
This configuration will query both ns1 and route53 for the list of zones they are managing and dynamically add them to the list being managed using the sources and targets corresponding to the ‘*’ section. See Dynamic Zone Config for details.
Static Zone Config
In cases where fine grained control is desired and the configuration of
individual zones varies zones can be an explicit list with each configured
zone listed along with its specific setup. As exemplified below alias zones
can be useful when two zones are exact copies of each other, with the same
configuration and records. YAML anchors are also helpful to avoid duplication
where zones share config, but not records.:
---
manager:
include_meta: True
max_workers: 2
validators:
enabled:
- strict
- best-practice
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./config
default_ttl: 3600
enforce_order: True
ns1:
class: octodns_ns1.Ns1Provider
api_key: env/NS1_API_KEY
route53:
class: octodns_route53.Route53Provider
access_key_id: env/AWS_ACCESS_KEY_ID
secret_access_key: env/AWS_SECRET_ACCESS_KEY
zones:
example.com.: &dual_target
sources:
- config
targets:
- ns1
- route53
# these have the same setup as example.com., but will have their own files
# in the configuration directory for records.
third.tv.: *dual_target
fourth.tv.: *dual_target
example.net.:
# example.net. is an exact copy of example.com., there will not be an
# example.net.yaml file in the config directory as `alias` includes
# duplicating the records of the aliased zone along with its config.
alias: example.com.
other.com.:
lenient: True
sources:
- config
targets:
- ns1
General Configuration Concepts
class is a special key that tells octoDNS what python class should be
loaded. Any other keys will be passed as configuration values to that
provider. In general any sensitive or frequently rotated values should come
from environmental variables. When octoDNS sees a value that starts with
env/ it will look for that value in the process’s environment and pass the
result along.
Further information can be found in the docstring of each source and provider class.
The include_meta key in the manager section of the config controls the
creation of a TXT record at the root of a zone that is managed by octoDNS. If
set to True, octoDNS will create a TXT record for the root of the zone with
the value provider=<target-provider>. If not specified, the default value for
include_meta is False.
The max_workers key in the manager section of the config enables threading
to parallelize the planning portion of the sync.
lenient
lenient mostly focuses on the details of Record``s and standards
compliance. When set to ``true octoDNS will allow non-compliant
configurations & values where possible. For example CNAME values that don’t end
with a ., label length restrictions, and invalid geo codes on dynamic
records. When in lenient mode octoDNS will log validation problems at
WARNING and try and continue with the configuration or source data as it
exists. See Lenience for more information on the concept and how it can be
configured.
Use lenient when you want all validation errors for a specific record or
zone converted to warnings — for example when importing legacy data that isn’t
fully compliant and you want to manage it as-is while you clean it up
incrementally.
Use disable_validators (see Disabling built-in validators below) when
you want to permanently skip a specific check across all records of a given
type — for example if a validator conflicts with your provider’s requirements or
your organisation’s conventions.
Validators
octoDNS ships with a suite of built-in validators that check records for correctness (valid TTLs, well-formed values, healthcheck protocol names, etc.) before any changes are applied. The validator system supports: enabling validator sets, adding custom validators, disabling individual validators, and registering validators programmatically from third-party code.
Validator sets and manager.validators.enabled
Validators belong to named sets. manager.validators.enabled controls
which sets are active for a run (default: ['legacy']):
manager:
validators:
enabled:
- legacy
Omitting manager.validators.enabled is equivalent to
enabled: [legacy] and preserves the original octoDNS behaviour. The
legacy set will remain the default until 2.x when strict and
best-practice become the defaults.
The recommended configuration is to opt in now:
manager:
validators:
enabled:
- strict
- best-practice
The strict set contains stricter validators that supersede their
legacy counterparts — use one or the other, not both, to avoid redundant
checks.
The best-practice set contains validators that enforce DNS best-practice
recommendations (rather than RFC requirements) and is independent of
strict/legacy. The recommended configuration is to enable both
strict and best-practice together.
A validator can belong to multiple sets; it becomes active when any of its
sets is listed in manager.validators.enabled.
Setting enabled: [] activates only validators whose sets is None
(see Attaching validators programmatically below).
Built-in validator ids
Validators with ids ending in -rfc enforce requirements derived directly
from an RFC. Ids prefixed with _ (e.g. _values-type) are internal
bridge validators with sets=None — always active and cannot be disabled.
Validators active in both legacy and strict:
id |
description |
|---|---|
|
Record name format (RFC 1035/2181) |
|
TTL range (positive integer) |
|
octoDNS healthcheck config fields |
|
CNAME must not be at zone root |
|
ALIAS must not be at zone root |
|
A / AAAA value format |
|
CNAME/ALIAS/DNAME/PTR target format |
|
NS targets format |
|
LOC rdata format (RFC 1876) |
|
TXT/SPF chunk encoding |
|
SVCB rdata format (RFC 9460) |
|
HTTPS rdata format (RFC 9460) |
``openpgpkey-value-rfc``| OPENPGPKEY rdata format (RFC 7929) |
|
|
Dynamic routing config (pools and rules) |
|
URLFWD rdata format |
|
CNAME and ALIAS cannot coexist with other records |
Validators active in legacy only (will be superseded in strict):
id |
description |
|---|---|
|
Geo routing config |
|
SRV name format |
|
URI name format |
|
CAA rdata format |
|
MX rdata format |
|
SSHFP algorithm/fingerprint format |
|
SRV rdata format |
|
URI rdata format |
|
NAPTR rdata format |
|
DS rdata format |
|
TLSA rdata format |
Validators active in strict only (stricter replacements):
id |
description |
|---|---|
|
SRV name strict per RFC 2782 + RFC 6335 §5.1;
replaces |
|
SRV rdata strict per RFC 2782 (range, null target);
replaces |
|
URI name strict per RFC 7553 + RFC 6335 §5.1;
replaces |
|
MX preference in [0, 65535]; null MX rules per
RFC 7505; replaces |
|
CAA flags restricted to 0/128 (RFC 8659 §4.1);
tag must match |
|
TLSA fields uint8 [0, 255]; certificate_association
_data must be hex; SHA-256/SHA-512 length enforced
(RFC 6698); replaces |
|
DS key_tag uint16, algorithm/digest_type uint8;
digest must be hex; SHA-1/SHA-256/SHA-384 length
enforced (RFC 4034/4509/6605); replaces |
|
NAPTR order/preference uint16; flags S/A/U/P only;
replacement must be FQDN or “.” (RFC 3403);
replaces |
|
SSHFP algorithm/fingerprint_type uint8 [0, 255];
fingerprint must be hex; SHA-1/SHA-256 length
enforced (RFC 4255/6594); replaces |
|
URI priority/weight uint16 [0, 65535] (RFC 7553 §4);
replaces |
|
CNAME must not be at the zone apex |
|
CNAME chains within the zone must not loop |
|
DNAME must not coexist with CNAME at any node, or with NS at a non-apex node. Also warns about occluded subordinate records. |
|
MX target must not be a CNAME |
|
NS target must not be a CNAME |
|
SRV target must not be a CNAME |
|
In-zone NS targets must have A/AAAA glue records |
|
CNAME/ALIAS/DNAME/PTR target must not be an IP |
|
NS targets must not be an IP |
|
SPF record type is deprecated in favor of TXT |
To opt into all strict validators at once:
manager:
validators:
enabled:
- strict
Validators active in best-practice only:
id |
description |
|---|---|
|
CNAME/ALIAS/DNAME target must end with
|
|
NS/PTR targets must each end with |
|
MX |
|
SRV |
|
SVCB |
|
HTTPS |
|
NAPTR |
|
CNAME target should not point to a CNAME |
|
In-zone MX targets must be resolvable |
|
In-zone SRV targets must be resolvable |
|
In-zone CNAME targets must be resolvable |
Validators active in best-practice only that check the entire zone:
id |
description |
|---|---|
|
Comprehensive best-practice validator for mail records (MX, SPF, DMARC). It supports ‘mail’ and ‘no-mail’ modes. In its default ‘auto’ mode it is a no-op if no mail records (MX, SPF, or DMARC) are found. If any are present it enforces ‘mail’ or ‘no-mail’ based on the presence of MX records at the apex. |
|
CAA records must have at least one issue or issuewild tag (if any CAA records exist) |
|
Zone must have at least two NS records |
The recommended configuration is to enable both sets:
manager:
validators:
enabled:
- strict
- best-practice
In 2.x strict and best-practice will become the default sets.
Adding validators via config
Custom validators are declared in a top-level validators: section (parallel
to providers: and processors:):
validators:
my-ttl-floor:
class: mymodule.MinTtlValidator
min_ttl: 300
types:
- MX
The class key specifies the dotted import path of a
RecordValidator or
ValueValidator subclass. The optional
types key restricts the validator to those record types; omitting it
registers the validator for all types ('*'). All other keys are passed as
keyword arguments to __init__ after the mandatory id (config key)
argument — including sets if set-based activation is desired.
Config-declared validators follow the same activation rules as built-in
validators: a validator with sets=None (the default) is always active; one
with an explicit sets value is activated when any of its sets appears in
manager.enabled. manager.validators can still be used to activate a
validator for additional record types beyond those listed under types.
Disabling built-in validators
Use this when you want to permanently skip a specific check across all
records of a given type. If you instead want to suppress all validation errors
for a particular record or zone while keeping every check active, use
lenient (see lenient above).
Individual built-in validators can be turned off under
manager.validators.record.disable_validators:
manager:
validators:
record:
disable_validators:
'*':
- healthcheck
MX:
- mx-value
'*' removes the validator from every record type; a type string removes it
only for that type. Bridge validators (_-prefixed ids) cannot be disabled
and will raise a config error if listed here.
Attaching validators programmatically
Third-party modules (providers, processors, plugins) can register validators at
import time without any config entry. There are two paths depending on whether
the validator belongs to a Record subclass / value class or stands on its
own.
For a custom Record subclass (or a custom value class), declare a
VALIDATORS class attribute. Record.register_type walks the new
class’s MRO collecting every VALIDATORS list it finds, plus
_value_type.VALIDATORS if defined, and registers each one against the
new type:
from octodns.record import Record, ValuesMixin
from octodns.record.validator import RecordValidator, ValueValidator
class FooValueValidator(ValueValidator):
def validate(self, value_cls, data, _type): ...
class FooValue(str):
VALIDATORS = [FooValueValidator('foo-value')]
...
class NoPublicFooValidator(RecordValidator):
def validate(self, record_cls, name, fqdn, data): ...
class FooRecord(ValuesMixin, Record):
_type = 'FOO'
_value_type = FooValue
VALIDATORS = [NoPublicFooValidator('no-public-foo')]
Record.register_type(FooRecord)
To attach a validator to an already-registered record type, call
Record.register_validator directly:
from octodns.record import Record
from octodns.record.validator import RecordValidator
class NoPublicMxValidator(RecordValidator):
def validate(self, record_cls, name, fqdn, data): ...
Record.register_validator(NoPublicMxValidator('no-public-mx'), types=['MX'])
types=None (the default) registers for all record types.
Set membership and activation. A validator’s sets attribute controls
when it becomes active. The default is sets=None, which means the
validator is always activated regardless of manager.enabled — the right
choice for most third-party validators that should always run. To opt a
validator into set-based filtering, pass an explicit sets at construction
time:
Record.register_validator(
StrictMxValidator('strict-mx', sets={'rfc'}), types=['MX']
)
That validator is then only active when manager.enabled includes 'rfc'.
As long as the module is imported before Manager initialises, the validator
will be in the available registry and activated appropriately.
strict_supports
strict_supports is a Provider level parameter that comes into play when
a provider has been asked to create a record that it is unable to support. The
simplest case of this would be record type, e.g. SSHFP not being supported
by AzureProvider. If such a record is passed to an AzureProvider as a
target the provider will take action based on the strict_supports. When
true it will throw an exception saying that it’s unable to create the
record, when set to false it will log at WARNING with information about
what it’s unable to do and how it is attempting to work around it. Other
examples of things that cannot be supported would be dynamic records on a
provider that only supports simple or the lack of support for specific geos in
a provider, e.g. Route53Provider does not support NA-CA-*.
It is worth noting that these errors will happen during the plan phase of things so that problems will be visible without having to make changes.
As of octoDNS 1.x strict_supports is on by default. You have the choice to
set strict_supports=false on a per provider basis to request that things warn
and continue in a best-effort fashion.
Configuring strict_supports
The strict_supports parameter is available on all providers and can be
configured in YAML as follows:
providers:
someprovider:
class: whatever.TheProvider
...
strict_supports: true
Automatic PTR generation
octoDNS supports automatically generating PTR records from the A/AAAA
records it manages. For more information see the Automatic PTR Generation
documentation.
JSON Schema for zone YAML files
octoDNS publishes a JSON Schema (Draft 2020-12) describing its zone YAML file format. It is generated from the currently registered record types on every docs build, so the schema matches the code in that release.
The schema is intended for editors and CI linters. octoDNS’s own validation is unchanged — it continues to handle error reporting with source context.
Available versions
Read the Docs serves a copy of the schema under each version’s _static
directory:
Bundled with this documentation — always matches the version of octoDNS whose docs you are viewing
Latest release — recommended for most users; tracks the most recent release
Development — tracks
mainA specific release, e.g. v2.0.0
Note
The legacy filename octodns.schema.json is still published alongside
octodns-zone.schema.json with identical content. Existing modelines or
yaml.schemas configs pointing at the old name keep working.
Opting in with a modeline
The yaml-language-server (used by the redhat.vscode-yaml extension and
other editors) honors a modeline at the top of a YAML file:
# yaml-language-server: $schema=https://octodns.readthedocs.io/en/stable/_static/octodns-zone.schema.json
---
www:
type: A
ttl: 300
value: 1.2.3.4
Editor configuration
In VS Code (or any editor that uses yaml-language-server) you can instead
associate the schema with a file pattern via yaml.schemas:
"yaml.schemas": {
"https://octodns.readthedocs.io/en/stable/_static/octodns-zone.schema.json": [
"zones/*.yaml"
]
}
Generating locally
The octodns-schema CLI prints or writes the same schema:
octodns-schema --output octodns-zone.schema.json
JSON Schema for the main config file
octoDNS also publishes a JSON Schema describing the main config.yaml
format: providers, processors, validators, secret_handlers, and zones. Built-in
core classes (YamlProvider, OwnershipProcessor, etc.) get full property
schemas; third-party plugin classes are accepted with any kwargs as long as
class is present.
Available versions
Opting in with a modeline
# yaml-language-server: $schema=https://octodns.readthedocs.io/en/stable/_static/octodns-config.schema.json
---
providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: ./zones
Editor configuration
"yaml.schemas": {
"https://octodns.readthedocs.io/en/stable/_static/octodns-config.schema.json": [
"config.yaml"
]
}
Generating locally
octodns-schema --kind config --output octodns-config.schema.json