Introduction
This is the documentation for NixOS-DNS which was built to decouple modules even more from NixOS hosts and is slowly escalating into anything nix*-dns. Please open a issue if you find something is missing.
Here you will find:
- automagically generated docs for the modules and utility functions (Work In Progress)
- general usage instructions
- a contributing/development guide
- debugging help
Usage
For all the modules options take a look here.
There is a quite elaborate example here, you can also use it as a template (nix flake init -t github:Janik-Haag/nixos-dns
).
classical nix
Please note that the following code uses colmena to retrieve the DNS information from the hosts configurations, and npins for version pinning. You can of course use nixos-dns without colmena, you'll just have to figure out how to reference nixosConfigurations.
# default.nix
let
pins = import ./npins;
in
{ pkgs ? import pins.nixpkgs { }
, colmena ? pins.colmena
, nixos-dns ? import pins.nixos-dns
}:
let
generate = nixos-dns.utils.generate pkgs;
in {
dns = generate.octodnsConfig {
config = {
providers = {
hetzner = {
class = "octodns_hetzner.HetznerProvider";
token = "env/HETZNER_DNS_API";
};
};
};
zones = {
"example.com." = nixos-dns.utils.octodns.generateZoneAttrs [ "hetzner" ];
};
dnsConfig = {
nixosConfigurations = (import "${colmena}/src/nix/hive/eval.nix" {
rawHive = (import ./hive.nix { inherit pkgs nixos-dns; });
}).nodes;
extraConfig = import ./dns.nix;
};
};
}
# hive.nix
{ pkgs, nixos-dns }: {
meta = {
nixpkgs = pkgs;
};
defaults = { pkgs, lib, config, ... }: {
imports = [
((import nixos-dns).nixosModules.default)
];
};
exampleHost = {
deployment.targetHost = "host.example.com";
deployment.tags = [ "infra" ];
imports = [
./hosts/exampleHost.nix
];
};
}
Now you just need to run nix-build -A dns
to realize the zonefiles and octodns config.
flakes
{
# You of course have to add the `nixos-dns` input like:
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixos-dns.url = "github:Janik-Haag/nixos-dns";
nixos-dns.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = inputs @ {
self,
nixpkgs,
nixos-dns
}: let
# You probably know this but flake outputs are architecture dependent,
# so we use this little helper function. Many people use `github:numtide/flake-utils` for that.
forAllSystems = nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" ];
in {
# Your NixOS configurations
nixosConfigurations = {
exampleHost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# Note that we are importing the nixos-dns module here
nixos-dns.nixosModules.dns
./hosts/exampleHost.nix
];
};
};
# We are adding this as package since the zoneFile output is architecture dependent
packages = forAllSystems (system:
let
# `nixos-dns.utils.generate` is a function taking in pkgs as argument
# and returns a set of functions that are architecture dependent like writing zoneFiles
generate = nixos-dns.utils.generate nixpkgs.legacyPackages.${system};
in
# the attrset `generate.zoneFiles` get passed here is the default interface for dnsConfigs with nixos-dns
# and anything from the debugging functions, `generate.octodnsConfig` and zoneFiles uses it.
zoneFiles = generate.zoneFiles {
inherit (self) nixosConfigurations;
extraConfig = import ./dns.nix;
};
}
)
};
}
NixOS Module
NixOS-DNS was built to decouple modules even more from their host.
To achieve this we have the concept of baseDomains
and subDomains
.
In a nixos hosts configuration you would do something like:
networking.hostName = "example-host";
networking.domains = {
enable = true;
baseDomains = {
"example.com" = {
a.data = "203.0.113.42";
aaaa.data = "2001:db8:1c1b:c00e::1";
};
};
};
This enables the networking.domains
module.
And registers the baseDomain example.com
with the a
and aaaa
records set.
You might notice the .data
behind any record, this is because you might want to set the ttl different based on record type.
As you can see above the .ttl
isn't specifically added to every record, this is because there is networking.domains.defaultTTL
So every record
has two fields ttl
and data
, the data type differs based on the record, for more info please refer to the module docs.
And inside of a module you would do something like:
networking.domains.subDomains."grafana.example.com" = { };
So this would produce this set:
{
"example.com" = {
"grafana.example.com" = {
a = {
ttl = 86400;
data = "203.0.113.42";
};
aaaa = {
ttl = 86400;
data = "2001:db8:1c1b:c00e::1";
};
};
};
}
note
baseDomains and their records don't end up in zone files, octodns configs, or any other output for that matter So in the example above for "example.com" to end up in a zone file you would have to add:
networking.domains.subDomains."example.com" = { };
to the hosts configuration.
Nix supports ${}
operations inside of attrsets, so you can get creative and do stuff like:
networking.domains.subDomains."${networking.hostname}.example.com" = { };
networking.domains.subDomains."*.${networking.hostname}.example.com" = { };
networking.domains.subDomains."${networking.hostname}.prometheus.example.com" = { };
NixOS-DNS does a bunch of magic to automatically map subDomains to their closest baseDomain and throws an error if there is no matching baseDomain. So if we have:
networking.domains.baseDomains = {
"example.net" = {};
"example.com" = {};
"domain.example.com" = {};
"subdomain.example.com" = {};
};
and:
networking.domains.baseDomains = {
"example.com" = {};
"mydomain.example.com" = {};
"cats.subdomain.example.com" = {};
};
We would get:
subDomains | matches |
---|---|
"example.com" | "example.com" |
"mydomain.example.com" | "example.com" |
"cats.subdomain.example.com" | "subdomain.example.com" |
And example.net
just wouldn't get matched, but that's fine since it is a baseDomain, if it were a subDomain it would cause an error.
extraConfig
You probably want to add some more information, to do so you can use the extraConfiguration key in the dnsConfig. Please take a look at the example for usage information.
All the hosts in nixosConfigurations
and extraConfig
get merged and nothing gets overwritten.
So if you define multiple domains with the same records all the record data gets merged.
octodns
NixOS-DNS has native octodns support.
To use it add a package like the zoneFiles one above just for octodns using generate.octodnsConfig
which expects a attrSet with a dnsConfig
, a config
and a zones key.
This would look like:
...
octodns = generate.octodnsConfig {
# this is the same attr we pass to zoneFiles
dnsConfig = {
inherit (self) nixosConfigurations;
extraConfig = import ./dns.nix;
};
# the octodns config key
config = {
providers = {
# adding a provider to push to
powerdns = {
class = "octodns_powerdns.PowerDnsProvider";
host = "ns.dns.invalid";
# reads the env var from your shell
api_key = "env/POWERDNS_API_KEY";
};
};
};
zones = {
# `nixos-dns.utils.octodns.generateZoneAttrs` is a helper function
# generating the correct values for usage with NixOS-DNS
# this is the only place in nixos-dns having trailing dots
# this was left in so we have a one to one map of the octodns values
"example.com." = nixos-dns.utils.octodns.generateZoneAttrs [ "powerdns" ];
"example.org." = nixos-dns.utils.octodns.generateZoneAttrs [ "powerdns" ];
"missing.invalid." = nixos-dns.utils.octodns.generateZoneAttrs [ "powerdns" ];
};
};
...
With the example above we at least need the octodns bind and powerdns provider. The powerdns provider is needed because it's used in the example. NixOS-DNS uses the bind provider internally, since it can reads zone-files, so we also need that. We do this because it is a lot less maintenance and less likely to have bugs instead of also maintaining a nixos-dns to octodns internal yaml builder.
You can run the example below to check if your config works, just make sure to do a nix build /your/nixos/config#octodns
before and replace example.com.
(note the trailing dot) with your domain.
nix-shell -p 'octodns.withProviders (ps: [ octodns-providers.bind octodns-providers.powerdns ])' --run "POWERDNS_API_KEY="" octodns-dump --config-file=./result --output-dir=/tmp/octodns_dump example.com. config"
cat /tmp/octodns_dump/example.com.yaml
Please refer to the octodns documentation for more information, the values should map one to one to nixos-dns.
Updating
We want to strongly encourage you to take a look at the CHANGELOG.md before updating. Other then that the updates should be as straight forward as any other NixOS updates.
Modules
There is a module for NixOS and a module for extraConfig. They both take the same record values and have the same assertions. Please take a look at the coming pages for a list of available options. I currently have trouble rendering the record fields if their type is a sub module, if you are interested in any like caa or mx, you'll sadly have to take a look at the source for now.
extraConfig
Takes in the extraConfig module.
Type: submodule
Default:
{ }
nixosConfigurations
Takes in the equivalent of the self.nixosConfigurations flake attribute.
Type: attribute set
Default:
{ }
networking.domains.enable
Whether to enable networking.domains.
Type: boolean
Default:
false
Example:
true
networking.domains.baseDomains
Attribute set of domains and records for the subdomains to inherit.
Type: attribute set of (submodule)
Default:
{ }
networking.domains.baseDomains.<name>.a
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.a.data
Commonly used to map a name to a list of IPv4 addresses.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
"9.9.9.9"
networking.domains.baseDomains.<name>.a.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.aaaa
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.aaaa.data
Commonly used to map a name to a list of IPv6 addresses.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
"2620:fe::fe"
networking.domains.baseDomains.<name>.aaaa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.alias
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.alias.data
Maps one domain name to another and uses the dns resolver of your dns server for responses.
Type: null or string or list of string
Default:
null
Example:
"foo.example.com"
networking.domains.baseDomains.<name>.alias.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.caa
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.caa.data
DNS Certification Authority Authorization, constraining acceptable CAs for a host/domain
Type: null or (submodule) or list of (null or (submodule))
Default:
null
networking.domains.baseDomains.<name>.caa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.cname
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.cname.data
Same as alias but the requesting party will have to resolve the response which can lead to more latency.
Type: null or string
Default:
null
Example:
"foo.example.com"
networking.domains.baseDomains.<name>.cname.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.dname
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.dname.data
Same as cname but also gets applied to any subdomain of the given domain
Type: null or string
Default:
null
Example:
"foo.example.com"
networking.domains.baseDomains.<name>.dname.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.mx
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.mx.data
List of mail exchange servers that accept email for this domain.
Type: null or (submodule) or list of (null or (submodule))
Default:
null
networking.domains.baseDomains.<name>.mx.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.ns
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.ns.data
Nameserver responsible for your zone. Note, that this option technically allows for only one name server but I would strongly advise against that.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
[
"ns1.example.com"
"ns2.example.com"
"ns3.example.com"
]
networking.domains.baseDomains.<name>.ns.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.soa
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.soa.data
Specifies authoritative information about a DNS zone.
Type: null or (submodule)
Default:
null
networking.domains.baseDomains.<name>.soa.data.expire
If a secondary server does not get a response from the primary server for this amount of time, it should stop responding to queries for the zone.
Type: signed integer
Example:
""
networking.domains.baseDomains.<name>.soa.data.mname
This is the name of the primary nameserver for the zone. Secondary servers that maintain duplicates of the zone’s DNS records receive updates to the zone from this primary server.
Type: string
Example:
"ns.example.com"
networking.domains.baseDomains.<name>.soa.data.refresh
The length of time secondary servers should wait before asking primary servers for the SOA record to see if it has been updated.
Type: signed integer
Example:
86400
networking.domains.baseDomains.<name>.soa.data.retry
The length of time a server should wait for asking an unresponsive primary nameserver for an update again.
Type: signed integer
Example:
""
networking.domains.baseDomains.<name>.soa.data.rname
Email of zone administrators.
Type: string
Example:
"noc@example.com"
networking.domains.baseDomains.<name>.soa.data.serial
A zone serial number is a version number for the SOA record (the higher the newer). When the serial number changes in a zone file, this alerts secondary nameservers that they should update their copies of the zone file via a zone transfer. Usually most dns-utilities working with zonefiles increment it automatically.
Type: signed integer
Example:
""
networking.domains.baseDomains.<name>.soa.data.ttl
Type: signed integer
Default:
cfg.defaultTTL
Example:
""
networking.domains.baseDomains.<name>.soa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.spf
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.spf.data
Spf record won’t be implemented due to deprecation in RFC 7208, please use a txt record
Type: unspecified value
Default:
null
networking.domains.baseDomains.<name>.spf.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.srv
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.srv.data
Specification of data in the Domain Name System defining the location, i.e., the hostname and port number, of servers for specified services. It is defined in RFC 2782.
Type: null or (submodule) or list of (null or (submodule))
Default:
null
networking.domains.baseDomains.<name>.srv.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.txt
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.txt.data
Just any string, commonly used to transfer machine readable metadata.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
"v=DMARC1; p=none"
networking.domains.baseDomains.<name>.txt.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.baseDomains.<name>.uri
Type: submodule
Default:
{ }
networking.domains.baseDomains.<name>.uri.data
Used for publishing mappings from hostnames to URIs.
Type: null or (submodule) or list of (null or (submodule))
Default:
null
networking.domains.baseDomains.<name>.uri.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains
Attribute set of subdomains that inherit values from their matching domain.
Type: attribute set of (submodule)
Default:
{ }
networking.domains.subDomains.<name>.a
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.a.data
Commonly used to map a name to a list of IPv4 addresses.
Type: null or ((list of string) or string convertible to it)
Default:
Automatically use the same record as the matching base domain
Example:
"9.9.9.9"
networking.domains.subDomains.<name>.a.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.aaaa
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.aaaa.data
Commonly used to map a name to a list of IPv6 addresses.
Type: null or ((list of string) or string convertible to it)
Default:
Automatically use the same record as the matching base domain
Example:
"2620:fe::fe"
networking.domains.subDomains.<name>.aaaa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.alias
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.alias.data
Maps one domain name to another and uses the dns resolver of your dns server for responses.
Type: null or string or list of string
Default:
Automatically use the same record as the matching base domain
Example:
"foo.example.com"
networking.domains.subDomains.<name>.alias.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.caa
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.caa.data
DNS Certification Authority Authorization, constraining acceptable CAs for a host/domain
Type: null or (submodule) or list of (null or (submodule))
Default:
Automatically use the same record as the matching base domain
networking.domains.subDomains.<name>.caa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.cname
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.cname.data
Same as alias but the requesting party will have to resolve the response which can lead to more latency.
Type: null or string or list of string
Default:
Automatically use the same record as the matching base domain
Example:
"foo.example.com"
networking.domains.subDomains.<name>.cname.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.dname
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.dname.data
Same as cname but also gets applied to any subdomain of the given domain
Type: null or string or list of string
Default:
Automatically use the same record as the matching base domain
Example:
"foo.example.com"
networking.domains.subDomains.<name>.dname.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.mx
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.mx.data
List of mail exchange servers that accept email for this domain.
Type: null or (submodule) or list of (null or (submodule))
Default:
Automatically use the same record as the matching base domain
networking.domains.subDomains.<name>.mx.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.ns
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.ns.data
Nameserver responsible for your zone. Note, that this option technically allows for only one name server but I would strongly advise against that.
Type: null or ((list of string) or string convertible to it)
Default:
Automatically use the same record as the matching base domain
Example:
[
"ns1.example.com"
"ns2.example.com"
"ns3.example.com"
]
networking.domains.subDomains.<name>.ns.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.soa
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.soa.data
Specifies authoritative information about a DNS zone.
Type: null or (submodule)
Default:
Automatically use the same record as the matching base domain
networking.domains.subDomains.<name>.soa.data.expire
If a secondary server does not get a response from the primary server for this amount of time, it should stop responding to queries for the zone.
Type: signed integer
Example:
""
networking.domains.subDomains.<name>.soa.data.mname
This is the name of the primary nameserver for the zone. Secondary servers that maintain duplicates of the zone’s DNS records receive updates to the zone from this primary server.
Type: string
Example:
"ns.example.com"
networking.domains.subDomains.<name>.soa.data.refresh
The length of time secondary servers should wait before asking primary servers for the SOA record to see if it has been updated.
Type: signed integer
Example:
86400
networking.domains.subDomains.<name>.soa.data.retry
The length of time a server should wait for asking an unresponsive primary nameserver for an update again.
Type: signed integer
Example:
""
networking.domains.subDomains.<name>.soa.data.rname
Email of zone administrators.
Type: string
Example:
"noc@example.com"
networking.domains.subDomains.<name>.soa.data.serial
A zone serial number is a version number for the SOA record (the higher the newer). When the serial number changes in a zone file, this alerts secondary nameservers that they should update their copies of the zone file via a zone transfer. Usually most dns-utilities working with zonefiles increment it automatically.
Type: signed integer
Example:
""
networking.domains.subDomains.<name>.soa.data.ttl
Type: signed integer
Default:
cfg.defaultTTL
Example:
""
networking.domains.subDomains.<name>.soa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.spf
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.spf.data
Spf record won’t be implemented due to deprecation in RFC 7208, please use a txt record
Type: unspecified value
Default:
Automatically use the same record as the matching base domain
networking.domains.subDomains.<name>.spf.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.srv
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.srv.data
Specification of data in the Domain Name System defining the location, i.e., the hostname and port number, of servers for specified services. It is defined in RFC 2782.
Type: null or (submodule) or list of (null or (submodule))
Default:
Automatically use the same record as the matching base domain
networking.domains.subDomains.<name>.srv.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.txt
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.txt.data
Just any string, commonly used to transfer machine readable metadata.
Type: null or ((list of string) or string convertible to it)
Default:
Automatically use the same record as the matching base domain
Example:
"v=DMARC1; p=none"
networking.domains.subDomains.<name>.txt.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
networking.domains.subDomains.<name>.uri
Type: submodule
Default:
{ }
networking.domains.subDomains.<name>.uri.data
Used for publishing mappings from hostnames to URIs.
Type: null or (submodule) or list of (null or (submodule))
Default:
Automatically use the same record as the matching base domain
networking.domains.subDomains.<name>.uri.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones
Takes in a attrset of domain apex and their entries.
Type: attribute set of attribute set of (submodule)
Default:
{ }
zones.<name>.<name>.a
Type: submodule
Default:
{ }
zones.<name>.<name>.a.data
Commonly used to map a name to a list of IPv4 addresses.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
"9.9.9.9"
zones.<name>.<name>.a.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.aaaa
Type: submodule
Default:
{ }
zones.<name>.<name>.aaaa.data
Commonly used to map a name to a list of IPv6 addresses.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
"2620:fe::fe"
zones.<name>.<name>.aaaa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.alias
Type: submodule
Default:
{ }
zones.<name>.<name>.alias.data
Maps one domain name to another and uses the dns resolver of your dns server for responses.
Type: null or string or list of string
Default:
null
Example:
"foo.example.com"
zones.<name>.<name>.alias.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.caa
Type: submodule
Default:
{ }
zones.<name>.<name>.caa.data
DNS Certification Authority Authorization, constraining acceptable CAs for a host/domain
Type: null or (submodule) or list of (null or (submodule))
Default:
null
zones.<name>.<name>.caa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.cname
Type: submodule
Default:
{ }
zones.<name>.<name>.cname.data
Same as alias but the requesting party will have to resolve the response which can lead to more latency.
Type: null or string
Default:
null
Example:
"foo.example.com"
zones.<name>.<name>.cname.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.dname
Type: submodule
Default:
{ }
zones.<name>.<name>.dname.data
Same as cname but also gets applied to any subdomain of the given domain
Type: null or string
Default:
null
Example:
"foo.example.com"
zones.<name>.<name>.dname.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.mx
Type: submodule
Default:
{ }
zones.<name>.<name>.mx.data
List of mail exchange servers that accept email for this domain.
Type: null or (submodule) or list of (null or (submodule))
Default:
null
zones.<name>.<name>.mx.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.ns
Type: submodule
Default:
{ }
zones.<name>.<name>.ns.data
Nameserver responsible for your zone. Note, that this option technically allows for only one name server but I would strongly advise against that.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
[
"ns1.example.com"
"ns2.example.com"
"ns3.example.com"
]
zones.<name>.<name>.ns.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.soa
Type: submodule
Default:
{ }
zones.<name>.<name>.soa.data
Specifies authoritative information about a DNS zone.
Type: null or (submodule)
Default:
null
zones.<name>.<name>.soa.data.expire
If a secondary server does not get a response from the primary server for this amount of time, it should stop responding to queries for the zone.
Type: signed integer
Example:
""
zones.<name>.<name>.soa.data.mname
This is the name of the primary nameserver for the zone. Secondary servers that maintain duplicates of the zone’s DNS records receive updates to the zone from this primary server.
Type: string
Example:
"ns.example.com"
zones.<name>.<name>.soa.data.refresh
The length of time secondary servers should wait before asking primary servers for the SOA record to see if it has been updated.
Type: signed integer
Example:
86400
zones.<name>.<name>.soa.data.retry
The length of time a server should wait for asking an unresponsive primary nameserver for an update again.
Type: signed integer
Example:
""
zones.<name>.<name>.soa.data.rname
Email of zone administrators.
Type: string
Example:
"noc@example.com"
zones.<name>.<name>.soa.data.serial
A zone serial number is a version number for the SOA record (the higher the newer). When the serial number changes in a zone file, this alerts secondary nameservers that they should update their copies of the zone file via a zone transfer. Usually most dns-utilities working with zonefiles increment it automatically.
Type: signed integer
Example:
""
zones.<name>.<name>.soa.data.ttl
Type: signed integer
Default:
cfg.defaultTTL
Example:
""
zones.<name>.<name>.soa.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.spf
Type: submodule
Default:
{ }
zones.<name>.<name>.spf.data
Spf record won’t be implemented due to deprecation in RFC 7208, please use a txt record
Type: unspecified value
Default:
null
zones.<name>.<name>.spf.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.srv
Type: submodule
Default:
{ }
zones.<name>.<name>.srv.data
Specification of data in the Domain Name System defining the location, i.e., the hostname and port number, of servers for specified services. It is defined in RFC 2782.
Type: null or (submodule) or list of (null or (submodule))
Default:
null
zones.<name>.<name>.srv.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.txt
Type: submodule
Default:
{ }
zones.<name>.<name>.txt.data
Just any string, commonly used to transfer machine readable metadata.
Type: null or ((list of string) or string convertible to it)
Default:
null
Example:
"v=DMARC1; p=none"
zones.<name>.<name>.txt.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
zones.<name>.<name>.uri
Type: submodule
Default:
{ }
zones.<name>.<name>.uri.data
Used for publishing mappings from hostnames to URIs.
Type: null or (submodule) or list of (null or (submodule))
Default:
null
zones.<name>.<name>.uri.ttl
The time to live (TTL) is a field on DNS records that tells you how long the record is valid (in seconds) and thus when it will be updated.
Type: signed integer
Default:
Automatically use the same ttl as the matching base domain
Example:
86400
Utils
These are a ton of helper functions that are used in NixOS-DNS internally, but you can also use them when working with dns in nix. Please take a look at the upcoming pages in this chapter for more information. Sadly no usage examples since I had trouble getting them to render properly with nixdoc and mdBook.
debug
utils.debug.host
Type: utils.debug.host :: nixosConfiguration -> AttrSet
Takes in one nixosConfiguration and returns a set of all the merged nixos-dns values.
host
: The host you want to debug
utils.debug.config
Type: utils.debug.config :: (AttrSet of dnsConfig) -> AttrSet
Function that returns the set of all the merged hosts and extraConfig
dnsConfig
: The dnsConfig module set
domains
utils.domains.getParts
Type: utils.domains.getParts :: String -> [ String ]
Convert a string like "example.org"
to a list like [ "example" "org" ]
domains
: String of a domain
utils.domains.comparePart
Type: utils.domains.compareParts :: String, Null -> String, Null -> Int
Compare domain parts and give them a value
If sub and base match they are valued 1
If sub and base don't match but base is null return 0
And in every other case return -1
sub
: the sub domain part you want to compare
base
: the base domain part you want to compare
utils.domains.comparableParts
Type: utils.domains.comparableParts :: String -> String -> { sub :: [ Null, String ], base [ Null, String ] }
uses fillList to generate two lists of domain parts, that are easily comparable. Will return attrSet like:
{
sub = [ "my" "fancy" "example" "com" ]
base = [ null null "example" "com" ]
}
subDomain
: subDomain you want to compare
baseDomain
: baseDomain you want to compare
utils.domains.rate
Type: utils.domains.rate :: [ String ] -> [ String ] -> [ Int ]
This returns a list like [ 0 (-1) 1 1 ]
Which contains the order comparison of
a sub domain and a base domain
subDomain
: expects a deconstructed domain like [ "example" "com" ]
baseDomain
: expects a deconstructed domain like [ "my" "example" "com" ]
utils.domains.construct
Type: utils.domains.construct :: [ String ] -> String
Expects a list of domain parts like [ "ns" "example" "com" ]
and builds a domain from it, in this case: ns.example.com
parts
: list of domain parts to construct
utils.domains.validateSubDomain
Type: utils.domains.validateSubDomain :: [ String ] -> [ String ] -> { valid :: Bool, value :: Int }
This returns a attrSet like
{
valid = true;
value = 2;
}
with valid
telling you if the sub domain corresponds to the base domain
and value
telling you how close it is (higher is better)
let's take for example: [ "my" "example" "com" ]
as sub domain and
[ "example" "com" ]
as base domain, this would return the attrSet shown above.
Because [ "example" "com" ]
will expand to [ null "example" "com" ]
and then
get rated like:
"my" == null = 0
"example" == "example" = 1
"com" == "com" = 1
the domain is valid since there is no negative value and the total value is 2
subDomain
: takes the same input as ratedDomain
baseDomain
: takes the same input as ratedDomain
utils.domains.getMostSpecific
Type: utils.domains.getMostSpecific :: String -> [ String ] -> String, Null
This function takes a sub domain and a list of domains,
and will find the most similar domain from the list.
It does this by comparing the domain parts and not singe letters
so if we have sub domain.example.com
and [ sub.example.com
example.com
]
then we would get example.com
as a result.
If the sub domain doesn't have a matching one in the list the function will return null
subDomain
: a string of a domain like "example.com"
baseDomains
: Function argument
utils.domains.mapBaseToSub
Type: utils.domains.mapBaseToSub :: String -> Attr -> String -> Any
This Functions uses getMostSpecific to get the value of a corresponding key for a sub domain
subDomain
: takes a attrSet like the one provided by networking.domains.subDomains
baseDomains
: takes a attrSet like the one provided by `networking.domains.baseDomains
value
: the key from which to get the value
utils.domains.getDomainsFromNixosConfigurations
Type: utils.domains.getDomainsFromNixosConfigurations :: Attr -> Attr
Be care full when using this, since you might end up overwriting previous results because if a key is defined multiple times only the last value will remain except if the value is a list then all of the content will be merged
nixosConfigurations
: Function argument
utils.domains.getDnsConfig
Type: utils.domains.getDnsConfig :: Attr -> Attr
Expects a attribute-set like:
{
inherit (self) nixosConfigurations darwinConfigurations;
extraConfig = import ./dns.nix;
}
it will do special casing for the keys nixosConfigurations (and potentially darwinConfiguratiosn) and every other key is expected to have a attrs that looks like the output of utils.debug it will then go ahead and merge all the dns configs into one.
config
: Function argument
general
utils.general.recursiveUpdateLists
Type: utils.general.recursiveUpdateLists :: [ Attr ] -> Attr
Function that merges sets the same as lib.recursiveUpdate
.
But if a value is a list it merges the list instead of overwriting it.
Stolen from here
attrList
: List of sets to merge
utils.general.fillList
Type: utils.general.recursiveUpdateLists :: [ Any ] -> Int -> [ Any ]
Prepends a list with a specific value X amount of times
For example say we have [ "my" "fancy" "example" "com" ]
And [ "example" "com" ]
but want to compare them, then
we can use this function like this:
let
lenSub = (builtins.length [ "my" "fancy" "example" "com" ]);
lenBase = (builtins.length [ "example" "com" ]);
in
fillList subDomain (lenBase - lenSub) null
which will result in: [ null null "example" "com" ]
list
: list to prepend
amount
: how often
value
: with what
generate
This is a bit of a special thing since unlike every other utils.*
namespace this is not a set but a function returning a set.
The only function input is pkgs and it expects the nixpkgs package set. This is done in this way to keep this functions pure.
utils.generate.zoneFiles
Type: utils.generate.zoneFiles :: Attr -> [ Files ]
Generates zonefiles from dnsConfig
config
: expects the dnsConfig module output as a input
utils.generate.linkZoneFiles
Type: utils.generate.linkZoneFiles :: Attr -> [ Files ]
config
: takes the output from utils.domains.getDnsConfig
utils.generate.octodnsConfig
Takes a Attrset like
{
inherit dnsConfig;
config = { };
zones = { };
# optionally
manager = { };
}
Everything except for dnsConfig is a 1:1 map of the octodns config yaml described in their docs.
config
: The required config
helper
octodns
utils.octodns.fakeSOA
Type: utils.octodns.fakeSOA :: Attr -> Attr
Just adds a dummy SOA record. It won't actually be used by anything. But the octodns bind module has a check for the validity of a zone-file and a zone-file MUST have a SOA record. Anyways, octodns will just ignore its existence and only sync supported records.
dnsConfig
: takes the dnsConfig module
utils.octodns.makeConfigAttrs
Type: utils.octodns.makeConfigAttrs :: Attr -> Attr
Same thing as generate.octodnsConfig but instead of returning a derivation it returns a set ready for converting it to a file.
settings
: Takes the same attrset input as generate.octodnsConfig
zonefiles
utils.zonefiles.formatTxtRecord
Type: utils.zonefiles.formatTxtRecord :: String -> String
Converts a string into a valid txt record so it's compliant with RFC 4408 This means it splits the string every 255 chars and surrounds it with quotation marks
txtString
: The String of a txt resource record
utils.zonefiles.convertRecordToStr
Type: utils.zonefiles.convertRecordToStr :: String -> Any -> String
attributeset Takes any record from the module and converts it to a fitting zonefile string
record
: Record type, like a or caa
value
: Record value, like "198.51.100.42"
utils.zonefiles.mkZoneString
Type: utils.zonefiles.mkZoneString :: Attr -> String
Converts a zone attributeset into a zonefile and returns a multiline string
entries
: Takes dnsConfig."your-domain.invalid"
utils.zonefiles.write
Type: utils.zonefiles.write :: String -> Attr -> File
Returns a zone-file from NixOS-DNS values Can nicely be used with lib.mapAttrsToList
domainName
: takes "your-domain.invalid"
domainValues
: takes dnsConfig."your-domain.invalid"
Debugging
nixos-dns
In the example/flake.nix you will find:
# nix eval .#dnsDebugHost
dnsDebugHost = nixos-dns.utils.debug.host self.nixosConfigurations.host1;
# nix eval .#dnsDebugConfig
dnsDebugConfig = nixos-dns.utils.debug.config dnsConfig;
Executing either of the two will print a attrset of all the merged values that will be used in your config. You can just copy them in to your own flake and change dnsConfig/host1 to the one you actually want to debug.
Note
You can pipe the output of
nix eval
to nixfmt for pretty printing and into bat for syntax highlighting That could look Likebat -pP -l=nix <(nix eval .#dnsDebugHost | nixfmt)
zone files
You can use named-checkzone
from the bind
package like:
# named-checkzone zonename zonefile
named-checkzone example.com result/example.com
to check the validity of your zone file.
octodns config
You can use octodns-dump as described in the octodns usage section. Other then that you are pretty much on your own, sorry. (but feel free to open a issue)
Contributing/Development
First of all thank you for considering to contribute to this Project. If you want to just fix a small thing or add a tiny function feel free to open a PR and I'll probably just merge it after review. You can of course also open a issue. If you are thinking about doing a larger thing, for example adding dnscontrol support consider opening a issue first or doing a draft PR so we can talk about implantation details beforehand.
Tooling | Usage Example |
---|---|
nixpkgs-fmt as formatter | nix fmt |
statix as linter | statix check |
nix-unit for unit tests | nix-unit --flake .#tests |
nixdoc for documentation | nix build .#docs && xdg-open result/index.html |
all of these are in the projects nix devshell so just run nix develop
or direnv allow
and they will be available in your shell.
modules
Uses the same module system as nixpkgs. Documentation builds fail if any description field is empty, so be sure to add one. If a default module value is not a primary data type but tries to evaluate a function add the defaultText string, otherwise documentation builds will fail.
Please document breaking changes in the CHANGELOG.md
utils
Every function is documented using nixdoc. Please refer to the nixdoc readme for a howto or copy existing functions.
And every function has at least one unit test, ensuring that it works.
You can find the tests in utils/test-*.nix
Modifying a existing function
- update unit-tests
- update documentation
- add breaking changes to
CHANGELOG.md
Adding a new function
- add a unit-test
- add documentation
Deleting a function
- remove corresponding unit-tests
- add breaking changes to
CHANGELOG.md
docs
The docs are being built using mdBook, the build process is abstracted away into a nix derivation that outputs a static rendering of the book.
You can find the book files in docs/book
, all the files there get copied into the book and can be written like any other mdBook.
While building the book, the deviations docs/utils.nix
and docs/modules.nix
also get built which generate the markdown for the utility functions and modules using nixdoc and the modules system builtin documentation system.
You can build the docs locally by doing:
nix build .#docs && xdg-open result/index.html
When adding any examples please use the resources linked in the table below: