Ansible role for building DNS servers using Unbound
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Simon Cornet 37adf72dfd
All checks were successful
ci/woodpecker/push/linting Pipeline was successful
docs: improve readme
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 17:29:32 +02:00
.woodpecker chore(package): update cr.simoncor.net/dockerhub/davidanson/markdownlint-cli2 docker tag to v0.23.3 2026-09-20 05:51:41 +00:00
defaults feat: make defaults configurable 2026-05-06 17:38:44 +02:00
handlers chore: migrate bare ansible_* facts to ansible_facts[] syntax 2026-05-19 09:19:08 +02:00
meta chore: rename meta/main.yml to meta/main.yaml 2026-02-16 10:44:28 +01:00
roles ci: migrate from gitlab ci to woodpecker 2026-05-15 14:13:49 +02:00
tasks chore: migrate bare ansible_* facts to ansible_facts[] syntax 2026-05-19 09:19:08 +02:00
templates/unbound style: remove empty whitespace lines from jinja2 templates 2026-05-20 17:51:40 +02:00
.ansible-lint fix: exclude only sops yml files from lint 2026-07-14 16:24:01 +02:00
.editorconfig chore: add .editorconfig and .gitattributes 2026-05-27 14:24:07 +02:00
.gitattributes chore: add .editorconfig and .gitattributes 2026-05-27 14:24:07 +02:00
.gitignore feat: initial commit 2025-07-14 14:19:39 +02:00
.markdownlint-cli2.jsonc ci: migrate from gitlab ci to woodpecker 2026-05-15 14:13:49 +02:00
.sops.yaml fix: correct sops regex to ya?ml 2026-07-14 16:02:08 +02:00
.yamllint chore: exclude .yml files from lint configs 2026-07-14 16:17:44 +02:00
AGENTS.md chore: add AGENTS.md for opencode agents 2026-02-16 10:06:22 +01:00
ansible.cfg chore: sync linting and config files from common 2026-07-14 11:33:16 +02:00
playbook.yaml chore(playbook): run galaxy install only on first host 2026-08-13 08:49:04 +02:00
readme.md docs: improve readme 2026-10-05 17:29:32 +02:00
renovate.json feat: initial commit 2025-07-14 14:19:39 +02:00

Ansible Role: Unbound

Installs and configures the Unbound DNS server as a caching resolver that forwards all queries to upstream servers and serves local zones with static records. The role is installed as dns (see roles/requirements.yml and meta/main.yaml).

Requirements

Operating System Package manager Service handling
Alpine Linux apk service
Debian family apt systemd

Dependencies

None (dependencies: []). roles/requirements.yml only lists this role itself as dns (https://git.simoncor.net/ansible/unbound.git).

Variables

Variable Required Default Description
unbound_verbosity No 1 Log verbosity
unbound_port No 53 Listen port
unbound_do_ip6 No true Answer queries over IPv6
unbound_do_tcp No true Answer queries over TCP
unbound_num_threads No 2 Threads; also used for the cache slab settings
unbound_so_reuseport No true Use SO_REUSEPORT
unbound_use_caps_for_id No true Randomise case in queries (use-caps-for-id)
unbound_qname_minimisation No true QNAME minimisation
unbound_harden_dnssec_stripped No true Require DNSSEC data for trust-anchored zones
unbound_harden_glue No true Only trust glue within the server's authority
unbound_hide_identity No true Do not answer identity queries
unbound_hide_version No true Do not answer version queries
unbound_cache_min_ttl No 5 Minimum cache TTL in seconds
unbound_cache_max_ttl No 86400 Maximum cache TTL in seconds
unbound_prefetch No true Prefetch cache entries before they expire
unbound_upstream_dns No ["8.8.8.8", "1.1.1.1"] Upstream DNS servers (forward zone .)
unbound_allow_access Yes one example network Networks allowed to query, see below
unbound_zones No [] Local DNS zones with records, see below

The default for unbound_allow_access is a single example entry (example-lan, 192.168.1.0/24); override it with your own networks. Unbound listens on all interfaces (interface-automatic) and only answers allowed networks.

unbound_allow_access

Key Required Description
name Yes Description, used as a comment
network Yes Network in CIDR notation (access-control)

unbound_zones

Key Required Default Description
zone Yes Zone name (without trailing dot)
type No static Unbound local-zone type, for example static or transparent
records Yes List of records, see below

Example

unbound_upstream_dns:
  - "1.1.1.1"
  - "8.8.8.8"

unbound_allow_access:
  - name: "internal-lan"
    network: "10.0.0.0/8"
  - name: "dmz"
    network: "192.168.1.0/24"

unbound_zones:
  - zone: "internal.example.com"
    type: "static"
    records:

      # A records (default type)
      - name: "server1.internal.example.com"
        value: "10.0.1.10"

      # AAAA record
      - name: "server2.internal.example.com"
        type: "AAAA"
        value: "2001:db8::1"

      # CNAME record
      - name: "internal.example.com"
        type: "CNAME"
        value: "server1.internal.example.com"

      # MX record
      - name: "internal.example.com"
        type: "MX"
        priority: 10
        value: "mail.internal.example.com"

      # TXT record
      - name: "internal.example.com"
        type: "TXT"
        value: "v=spf1 include:internal.example.com ~all"

      # SRV record
      - name: "_sip._tcp.internal.example.com"
        type: "SRV"
        priority: 10
        weight: 60
        port: 5060
        value: "server1.internal.example.com"

      # PTR record (manual reverse DNS)
      - name: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"
        type: "PTR"
        value: "server1.internal.example.com"

Supported Record Types

Records are written as local-data entries. The type defaults to A; any other type value that is not listed below is rendered as <name>. IN <type> <value>.

Type Required Fields Description
A (default) name, value IPv4 address record
AAAA name, value, type IPv6 address record
CNAME name, value, type Canonical name
MX name, value, type, priority Mail exchange
TXT name, value, type Text record
SRV name, value, type, priority, weight, port Service locator
PTR name, value, type Pointer record

Reverse DNS

Reverse DNS (PTR) records are automatically generated from IPv4 A records in zones whose type is not transparent, for addresses in these networks:

  • 192.168.x.x: one reverse zone per /24 (for example 1.168.192.in-addr.arpa)
  • 10.8.x.x: reverse zone 8.10.in-addr.arpa
  • 10.0.x.x: reverse zone 0.10.in-addr.arpa

Addresses in other networks get no PTR record. IPv6 reverse DNS is not auto-generated. Use manual PTR records in a dedicated zone instead.

Usage

Run playbook.yaml through Semaphore. The playbook first runs ansible-galaxy install -f -r roles/requirements.yml on localhost, then includes the dns role. It runs with serial: 1, so hosts are configured one at a time.

Operational notes

  • The role installs unbound, renders /etc/unbound/unbound.conf and /etc/unbound/zones.conf (the main file includes the second) and restarts and enables the service on every change.
  • All other queries are forwarded to unbound_upstream_dns.
  • There are no tags.