You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Mathieu Pasquet 547928426b Add my changes 2 years ago
defaults Use defaults/main.yml to avoid checking for empty lists everywhere 5 years ago
handlers Add my changes 2 years ago
meta Refresh doc for buster 2 years ago
tasks Add my changes 2 years ago
templates Add my changes 2 years ago
LICENSE Add LICENSE 5 years ago Add my changes 2 years ago

This is a modified version of "Ansible role for NSD"

Original source:

I have made several changes to fit my usecase (essentially remote control and dnssec specificities, as well as some cosmetic changes).

Changes: added two new dictionaries:

  • nsd_remote_control for remote controllong options in the config file
  • nsd_zone_subdirs to transfer whole subdirectories (I expect each one to contain a script which signs dnssec, installed as a cron)

This Ansible role installs and configure NSD, an authoritative DNS server. It also allows to publish DNS zones into NSD.


This role supports both NSD3 and NSD4, allows to manage both NSD configuration and zone files, in master or slave mode, with or without zone transfer (TSIG).

Most notably, compared to other NSD roles (elnappo, reallyenglish, creicm, adarnimrod, hudecof), it has the following features:

  • allows to store zone data in "classical" zone files, instead of having to write zones as ansible variables like here
  • supports both master and slave scenarios, or even a mixture of both (some zones running as master and some zones running as slave, on the same NSD server)
  • completely generic NSD configuration (you are free to define any configuration attribute supported by NSD, there is no hard-coded list in the role). This is similar to here but with a simpler syntax (automatic expansion of lists)
  • supports zone transfers: TSIG keys, notify slaves...
  • hopefully flexible yet simple to use!

However, it does not handles other things like firewall configuration or emails, and only supports Debian for now.


Note: all these use-cases can be used independently for each zone! That is, a single NSD server can be both a master for and a slave for

Pure multi-master

If all your DNS servers are configured with Ansible, this is the simplest setup: all servers are masters for the zone, and zone data is deployed using Ansible.

In this setup, there is no need for DNS-based zone transfers.

Multi-master with additional slaves not configured by this role

In this setup, one or more master are configured using Ansible, like in the previous use-case. However, DNS-based zone transfer is also setup, to allow external slaves to fetch zone data from a master. Whenever Ansible updates a zone on the masters, it will tell NSD to notify the slaves.

Note that in this setup, it is your responsibility to configure the slaves appropriately (accept notify from all masters, and fetch zone data from one or more masters).

Slave only

In this setup, the NSD servers are simply configured as slaves for the zone. In this case, no zone data is copied to the servers, because data will be fetched from an external master using normal DNS zone transfer mechanisms.

Note that in this setup, it is your responsibility to configure the master appropriately (notify the slaves, and allow zone transfers from the slaves).


This role has been tested on Debian (wheezy, jessie, stretch, buster), but could be adapted to work on other systems. Notably, this role does not setup nsd-control because this is already done automatically by the Debian package.

Role variables

This documents all role variables that you can set in your playbooks. See the end of this README for a complete example.

Server configuration

nsd_server_config [dict]

Dictionary of key-value pairs that will be added to the server: configuration section of NSD. A value can be either a string or a list. In the latter case, the value will be expanded into multiple configuration entries.

You can add any configuration option you want in this dictionary, but make sure that it is a configuration option understood by NSD! As a safety net, the role asks NSD to verify the generated configuration before proceeding, but this will not be done when running ansible-playbook --check.

nsd_local_server_config [dict]

Same syntax and semantic as nsd_server_config. This second variable is provided so that it's easier to add specific configuration to a single machine. You would typically define nsd_server_config in group_vars or in a playbook, while nsd_local_server_config would be defined in host_vars.

TSIG keys

nsd_tsig_keys [list of dict]

Optional list of TSIG keys. Each TSIG key must be a dictionary with the following attributes:

  • tsig_keyname: name of this TSIG key. Note that in a master/slave DNS configuration, the name of the TSIG key must be the same on master and slaves! This name is also used to refer to TSIG keys from the nsd_primary_zones and nsd_secondary_zones role variables. Mandatory.
  • tsig_secret: base64-encoded value of the key. Mandatory.
  • tsig_algorithm: algorithm used by the key, for instance hmac-md5. Mandatory.

Primary zones

nsd_primary_zones [list of dict]

List of zones to be served as master. Each zone must be a dictionary with the following attributes:

  • zone_name: name of the zone, for instance or Mandatory.
  • zone_filename: name of the file containing zone data (will be searched for in files/nsd/). Mandatory.
  • slaves: list of DNS slaves, format is described below. Optional.

The format for a slave entry is the following:

  • ip: IPv4 or IPv6 address of the DNS slave. Will be used to send "notify" messages, and to allow zone transfers from this IP. Mandatory.
  • tsig_key: name of the TSIG key to use when communicating with this slave. The name must match the tsig_keyname field of a previously-defined TSIG key, see above. Optional.

Secondary zones

nsd_secondary_zones [list of dict]

List of zones to be served as slave. Each zone must be a dictionary with the following attributes:

  • zone_name: name of the zone, for instance or Mandatory.
  • masters: list of DNS masters, format is described below. Optional but a slave zone is quite useless without master.

The format for a master entry is the following:

  • ip: IPv4 or IPv6 address of the DNS master. Will be used to request zone transfers, and to allow notify messages. Mandatory.
  • tsig_key: name of the TSIG key to use when communicating with this master. The name must match the tsig_keyname field of a previously-defined TSIG key, see above. Optional.

Advanced configuration variables

These variables should not need to be changed in most cases. Variables are presented here with their default value.

nsd_version: 4

The version of NSD. Used to skip tasks or handlers that do not make sense depending on the version.

nsd_service_name: "nsd"

The name of the init service, used to restart NSD.

nsd_pkg_name: "nsd"

Name of the Debian package to install.

nsd_control_program: "/usr/sbin/nsd-control"

Program used to control NSD, for reload, rebuild, notify...

nsd_config_dir: "/etc/nsd"

Directory where NSD configuration will be stored.

nsd_zones_config_file: "/etc/nsd/zones.conf"

Name of the config file that will hold zone configuration (it is then included from the main NSD configuration file).

nsd_primary_zones_dir: "/etc/nsd/primary"

Directory where the zone files will be copied by this role.

nsd_secondary_zones_dir: "/etc/nsd/secondary"

Directory where slave zone files will be placed by NSD after zone transfer.

Example playbook

This is a complete example playbook with several TSIG keys and several DNS zones: the first zone is a primary zone with no slave, the second zone has two slaves, and the third zone is a secondary zone with two masters.

- hosts: dnsservers
    - nsd
      verbosity: 2
      ip4-only: 'yes'
    # Two TSIG keys, used in zone definition below
      - tsig_keyname: ""
        tsig_secret: "3znH//y866vzpOZdahYYUlWeiY4iidiJGFRX6CI6OkUBggRNYFpZAMvlYbtnUosiBVPsgghA6zT0TzOEX0vetQ=="
        tsig_algorithm: hmac-md5
      - tsig_keyname: ""
        tsig_secret: "t6ELXqsSLYl57iO2rxj+X9+DNpOV3exTBFWu9wS/3jI="
        tsig_algorithm: hmac-sha256
    # Master zone without slave
      - zone_name: ""
        zone_filename: ""
    # Master zone with two slaves, one dual-stacked with a TSIG key, and one single-stacked with no key
      - zone_name: ""
        zone_filename: ""
          - ip: 2001:db8:42:1337::1
            tsig_key: ""
          - ip:
            tsig_key: ""
          - ip:
    # Slave zone with two masters, first one without key, second one with a key
      - zone_name: ""
          - ip:
          - ip: 2001:db8:1234:5678::9
            tsig_key: ""

Also in host_vars/ns1.yml:

  ip-address: ['2001:db8:ffff::42', '']

The zone data for the two primary zones needs to be stored in files/nsd/ at the root of your ansible directory:

# ls files/nsd/
# head -3 files/nsd/
$TTL 3h
@  IN  SOA  ns1 (2017090101 1d 2h 4w 1h)

Example advanced configuration

If you need more advanced customization, you can use the advanced variables. For instance, to support NSD3 on Debian wheezy, the appropriate configuration was:

nsd_version: 3
nsd_service_name: "nsd3"
nsd_pkg_name: "nsd3"
nsd_control_program: "/usr/sbin/nsdc"
nsd_config_dir: "/etc/nsd3"
nsd_zones_config_file: "/etc/nsd3/zones.conf"
nsd_primary_zones_dir: "/etc/nsd3/primary"
nsd_secondary_zones_dir: "/etc/nsd3/secondary"

This could be placed in a group_vars/wheezy.yml file or equivalent.


For a given zone, all machines using this role must be either all masters or all slaves.

This greatly simplifies configuration, and there is generally no need to have masters and slaves for the same zone handled by Ansible, since Ansible can just push the zone data to all servers (so, they can all be masters).

There are cases where having multiple masters whose zone is pushed by Ansible would probably not be desirable:

  • dynamic DNS records (which isn't supported by NSD anyway)
  • DNSSEC (you could always generate DNSSEC signatures in ansible, but it's awkward)

Thus, these use-cases are not supported.

Of course, you can workaround this limitation by simply calling this role multiple times with different machines and different configuration.