Deploy Knot DNS using Docker containers orchestrated by Ansible for automated, reproducible DNS infrastructure on Linux. This guide combines containerized deployment with Ansible automation for scalable DNS management.
ansible-galaxy collection install community.docker
Create an inventory file for your Knot DNS servers:
# inventory.ini
[knot_dns_servers]
dns-primary ansible_host=192.168.1.10
dns-secondary ansible_host=192.168.1.11
[knot_dns_servers:vars]
knot_version="3.5"
knot_config_dir="/opt/knotdns"
---
# deploy-knotdns-docker.yml
- name: Deploy Knot DNS with Docker
hosts: knot_dns_servers
become: true
gather_facts: true
vars:
# Knot DNS Docker image (official tag format)
knot_image: "cznic/knot:3.5"
knot_container_name: "knotdns"
# Directory structure
knot_config_dir: "/opt/knotdns"
knot_config_subdir: "{{ knot_config_dir }}/config"
knot_zones_subdir: "{{ knot_config_dir }}/zones"
knot_logs_subdir: "{{ knot_config_dir }}/logs"
knot_rundir_subdir: "{{ knot_config_dir }}/rundir"
# Server configuration
knot_server_workers: "{{ ansible_processor_vcpus | default(2) }}"
knot_zones:
- name: "example.com"
file: "{{ knot_zones_subdir }}/example.com.zone"
dnssec_signing: false
tasks:
- name: Install Docker dependencies
package:
name:
- docker
- docker-compose-plugin
state: present
when: ansible_os_family in ['Debian', 'RedHat']
- name: Ensure Docker service is running
systemd:
name: docker
state: started
enabled: true
- name: Create directory structure
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "{{ knot_config_dir }}"
- "{{ knot_config_subdir }}"
- "{{ knot_zones_subdir }}"
- "{{ knot_logs_subdir }}"
- "{{ knot_rundir_subdir }}"
- name: Deploy Docker Compose file
template:
src: docker-compose.yml.j2
dest: "{{ knot_config_dir }}/docker-compose.yml"
mode: '0644'
- name: Deploy Knot DNS configuration
template:
src: knot.conf.j2
dest: "{{ knot_config_subdir }}/knot.conf"
mode: '0640'
notify: Restart Knot DNS
- name: Deploy sample zone file
template:
src: zone.j2
dest: "{{ knot_zones_subdir }}/example.com.zone"
mode: '0640'
notify: Restart Knot DNS
- name: Start Knot DNS container
community.docker.docker_compose_v2:
project_src: "{{ knot_config_dir }}"
state: present
- name: Wait for Knot DNS to be ready
wait_for:
timeout: 10
delegate_to: localhost
- name: Verify Knot DNS is responding
command: docker exec {{ knot_container_name }} kdig @127.0.0.1 example.com
register: dig_result
retries: 5
delay: 3
until: dig_result.rc == 0
changed_when: false
handlers:
- name: Restart Knot DNS
community.docker.docker_compose_v2:
project_src: "{{ knot_config_dir }}"
state: present
restarted: true
Create templates/docker-compose.yml.j2:
version: '3.8'
services:
knotdns:
image: {{ knot_image }}
container_name: {{ knot_container_name }}
restart: unless-stopped
network_mode: "host"
command: knotd -c /config/knot.conf
volumes:
- {{ knot_config_subdir }}:/config:ro
- {{ knot_zones_subdir }}:/storage:rw
- {{ knot_logs_subdir }}:/var/log/knot:rw
- {{ knot_rundir_subdir }}:/rundir:rw
environment:
- TZ=Etc/UTC
healthcheck:
test: ["CMD", "knotc", "conf-check"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
deploy:
resources:
limits:
memory: 1G
cpus: '1.0'
Create templates/knot.conf.j2:
# Knot DNS Configuration
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
server:
listen: [ 0.0.0.0@53, ::@53 ]
workers: {{ knot_server_workers }}
rundir: /rundir
user: knot:knot
database:
storage: /storage
{% for zone in knot_zones %}
zone:
- domain: {{ zone.name }}
file: {{ zone.file }}
{% if zone.dnssec_signing is defined %}
dnssec-signing: {{ zone.dnssec_signing }}
{% endif %}
{% endfor %}
log:
- target: syslog
any: info
Create templates/zone.j2:
$ORIGIN example.com.
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
{{ ansible_date_time.epoch | int }}01 ; serial
7200 ; refresh (2 hours)
3600 ; retry (1 hour)
1209600 ; expire (2 weeks)
3600 ) ; minimum (1 hour)
IN NS ns1.example.com.
IN NS ns2.example.com.
ns1 IN A {{ ansible_default_ipv4.address }}
ns2 IN A 192.168.1.11
www IN A {{ ansible_default_ipv4.address }}
IN MX 10 mail.example.com.
# Run the complete deployment
ansible-playbook -i inventory.ini deploy-knotdns-docker.yml
# Run with specific tags
ansible-playbook -i inventory.ini deploy-knotdns-docker.yml --tags "config"
# Dry-run to check changes
ansible-playbook -i inventory.ini deploy-knotdns-docker.yml --check
For zero-downtime updates across multiple servers:
# Update one server at a time
ansible-playbook -i inventory.ini deploy-knotdns-docker.yml --serial 1
# Check container status
docker ps | grep knotdns
# Test DNS resolution
docker exec knotdns kdig @127.0.0.1 example.com
# Check configuration
docker exec knotdns knotc conf-check
# In your playbook vars section
knot_zones:
- name: "example.com"
file: "{{ knot_zones_subdir }}/example.com.zone"
dnssec_signing: true
- name: "internal.example.com"
file: "{{ knot_zones_subdir }}/internal.example.com.zone"
dnssec_signing: false
For environments where host networking is not desired:
# In docker-compose.yml.j2
networks:
knot_network:
driver: bridge
services:
knotdns:
# ... other configuration ...
network_mode: "bridge"
ports:
- "53:53/udp"
- "53:53/tcp"
v3.5.3, not latestno-new-privileges and read_only enabled# Encrypt sensitive variables
ansible-vault encrypt_string --name 'knot_tsig_secret' 'your-secret-key-here'
# Add to your playbook
- name: Check Knot DNS health
command: docker exec {{ knot_container_name }} knotc status
register: health_status
failed_when: health_status.rc != 0
- name: Backup Knot DNS configuration
archive:
path:
- "{{ knot_config_subdir }}"
- "{{ knot_zones_subdir }}"
dest: "/backup/knotdns-{{ ansible_date_time.date }}.tar.gz"
- name: Update Knot DNS image
community.docker.docker_image:
name: "cznic/knot:{{ knot_version }}"
source: pull
notify: Restart Knot DNS
Port binding fails:
sudo ss -tulnp | grep :53network_mode: "host" is setPermission errors:
Configuration validation fails:
docker exec knotdns knotc conf-check
# Check container logs
docker logs knotdns
# Enter container for debugging
docker exec -it knotdns sh
# Test DNS resolution
docker exec knotdns kdig @127.0.0.1 example.com
# View server status
docker exec knotdns knotc status
For automated deployments:
We develop tailored automation solutions for:
Let’s discuss your requirements: office@linux-server-admin.com | Contact