Deploy PowerDNS using Ansible with production-ready configurations for the Authoritative Server, Recursor, and DNSdist components. This guide provides playbooks for automated deployment and management.
---
- name: Install PowerDNS with official repository
hosts: powerdns
become: true
vars:
powerdns_version: "50" # Use 50 for 5.x series, 49 for 4.9.x series
powerdns_components:
- pdns-server # Authoritative server
powerdns_backend: "pdns-backend-sqlite3" # Options: pdns-backend-mysql, pdns-backend-pgsql, pdns-backend-sqlite3
powerdns_config_dir: "/etc/powerdns"
powerdns_database: "sqlite3"
tasks:
- name: Add PowerDNS repository (Debian/Ubuntu)
block:
- name: Install prerequisites
apt:
name:
- gnupg
- curl
- apt-transport-https
state: present
update_cache: yes
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Create keyrings directory
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Import PowerDNS GPG key (Key ID: FD380FBB)
ansible.builtin.key:
url: https://repo.powerdns.com/FD380FBB-pub.asc
keyring: /etc/apt/keyrings/pdns-archive-keyring.gpg
state: present
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Add PowerDNS repository
apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/pdns-archive-keyring.gpg] https://repo.powerdns.com/{{ ansible_distribution|lower }}-{{ ansible_distribution_release }}-auth-{{ powerdns_version }}/ {{ ansible_distribution_release }} main"
state: present
filename: pdns
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Add APT pinning for PowerDNS packages
copy:
dest: /etc/apt/preferences.d/pdns
content: |
Package: pdns-*
Pin: origin repo.powerdns.com
Pin-Priority: 600
mode: "0644"
when: ansible_distribution in ['Debian', 'Ubuntu']
when: ansible_os_family == "Debian"
- name: Add PowerDNS repository (RHEL/CentOS/AlmaLinux/Rocky)
block:
- name: Install EPEL release
package:
name: epel-release
state: present
when: ansible_os_family == "RedHat"
- name: Add PowerDNS repository (Enterprise Linux)
get_url:
url: "https://repo.powerdns.com/repo-files/el-auth-{{ powerdns_version }}.repo"
dest: "/etc/yum.repos.d/powerdns-auth-{{ powerdns_version }}.repo"
mode: "0644"
when: ansible_os_family == "RedHat"
when: ansible_os_family == "RedHat"
- name: Update package cache (Debian/Ubuntu)
apt:
update_cache: yes
when: ansible_os_family == "Debian"
- name: Install PowerDNS Authoritative Server
package:
name:
- "{{ item }}"
state: present
loop:
- "{{ powerdns_components }}"
- "{{ powerdns_backend }}"
notify: restart pdns
- name: Create PowerDNS configuration directory
file:
path: "{{ powerdns_config_dir }}"
state: directory
mode: "0755"
owner: pdns
group: pdns
- name: Enable and start PowerDNS service
systemd:
name: pdns
state: started
enabled: true
daemon_reload: yes
- name: Verify PowerDNS installation
command: "pdns_server --version"
register: pdns_version_output
changed_when: false
failed_when: false
- name: Display PowerDNS version
debug:
msg: "PowerDNS version: {{ pdns_version_output.stdout }}"
when: pdns_version_output.stdout is defined
---
- name: Configure PowerDNS with database backend
hosts: powerdns
become: true
vars:
powerdns_config_dir: "/etc/powerdns"
powerdns_database: "postgresql" # Options: mysql, postgresql, sqlite3
powerdns_db_host: "localhost"
powerdns_db_user: "pdns"
powerdns_db_password: "secure_password_here"
powerdns_db_name: "pdns"
powerdns_api_key: "your_secure_api_key_here"
powerdns_web_password: "your_secure_web_password"
powerdns_default_soa_name: "a.misconfigured.dns.server.example.com"
powerdns_default_soa_mail: "hostmaster@example.com"
handlers:
- name: restart pdns
systemd:
name: pdns
state: restarted
daemon_reload: yes
tasks:
- name: Install database client packages
package:
name:
- "{{ 'postgresql-client' if powerdns_database == 'postgresql' else '' }}"
- "{{ 'mysql-client' if powerdns_database == 'mysql' else '' }}"
state: present
when: powerdns_database != "sqlite3"
- name: Create database schema (MySQL)
block:
- name: Create MySQL database
community.mysql.mysql_db:
name: "{{ powerdns_db_name }}"
state: present
login_host: "{{ powerdns_db_host }}"
login_user: "{{ mysql_admin_user | default('root') }}"
login_password: "{{ mysql_admin_password }}"
when: powerdns_database == "mysql"
- name: Create MySQL user
community.mysql.mysql_user:
name: "{{ powerdns_db_user }}"
password: "{{ powerdns_db_password }}"
priv: "{{ powerdns_db_name }}.*:ALL"
host: "{{ powerdns_db_host }}"
state: present
login_host: "{{ powerdns_db_host }}"
login_user: "{{ mysql_admin_user | default('root') }}"
login_password: "{{ mysql_admin_password }}"
when: powerdns_database == "mysql"
- name: Download MySQL schema from GitHub
get_url:
url: "https://raw.githubusercontent.com/PowerDNS/pdns/master/modules/gmysqlbackend/schema.mysql.sql"
dest: "/tmp/pdns_schema.mysql.sql"
mode: "0644"
when: powerdns_database == "mysql"
- name: Import MySQL schema
community.mysql.mysql_db:
name: "{{ powerdns_db_name }}"
state: import
target: /tmp/pdns_schema.mysql.sql
login_host: "{{ powerdns_db_host }}"
login_user: "{{ powerdns_db_user }}"
login_password: "{{ powerdns_db_password }}"
when: powerdns_database == "mysql"
- name: Clean up temporary schema file
file:
path: /tmp/pdns_schema.mysql.sql
state: absent
when: powerdns_database == "mysql"
when: powerdns_database == "mysql"
- name: Create database schema (PostgreSQL)
block:
- name: Create PostgreSQL database
postgresql_db:
name: "{{ powerdns_db_name }}"
state: present
login_host: "{{ powerdns_db_host }}"
login_user: "{{ postgres_admin_user | default('postgres') }}"
login_password: "{{ postgres_admin_password | default(omit) }}"
when: powerdns_database == "postgresql"
- name: Create PostgreSQL user
postgresql_user:
name: "{{ powerdns_db_user }}"
password: "{{ powerdns_db_password }}"
db: "{{ powerdns_db_name }}"
priv: "ALL"
state: present
login_host: "{{ powerdns_db_host }}"
login_user: "{{ postgres_admin_user | default('postgres') }}"
login_password: "{{ postgres_admin_password | default(omit) }}"
when: powerdns_database == "postgresql"
- name: Download PostgreSQL schema from GitHub
get_url:
url: "https://raw.githubusercontent.com/PowerDNS/pdns/master/modules/gpgsqlbackend/schema.pgsql.sql"
dest: "/tmp/pdns_schema.pgsql.sql"
mode: "0644"
when: powerdns_database == "postgresql"
- name: Import PostgreSQL schema
postgresql_db:
name: "{{ powerdns_db_name }}"
state: import
target: /tmp/pdns_schema.pgsql.sql
login_host: "{{ powerdns_db_host }}"
login_user: "{{ powerdns_db_user }}"
login_password: "{{ powerdns_db_password }}"
when: powerdns_database == "postgresql"
- name: Clean up temporary schema file
file:
path: /tmp/pdns_schema.pgsql.sql
state: absent
when: powerdns_database == "postgresql"
when: powerdns_database == "postgresql"
- name: Configure PowerDNS main settings
template:
src: pdns.conf.j2
dest: "{{ powerdns_config_dir }}/pdns.conf"
owner: pdns
group: pdns
mode: "0640"
notify: restart pdns
- name: Enable and start PowerDNS service
systemd:
name: pdns
state: started
enabled: true
daemon_reload: yes
- name: Wait for service to be ready
wait_for:
port: 53
host: "{{ ansible_default_ipv4.address }}"
delay: 5
timeout: 30
delegate_to: localhost
Create this template file in your Ansible roles/templates directory:
# PowerDNS Configuration
# Generated by Ansible
# Basic settings
local-address={{ ansible_default_ipv4.address|default('0.0.0.0') }}
local-port=53
setuid=pdns
setgid=pdns
daemon=yes
socket-dir=/var/run/pdns
# Backend configuration
launch=g{{ powerdns_database }}
{% if powerdns_database == 'mysql' %}
gmysql-host={{ powerdns_db_host }}
gmysql-user={{ powerdns_db_user }}
gmysql-password={{ powerdns_db_password }}
gmysql-dbname={{ powerdns_db_name }}
{% elif powerdns_database == 'postgresql' %}
gpgsql-host={{ powerdns_db_host }}
gpgsql-user={{ powerdns_db_user }}
gpgsql-password={{ powerdns_db_password }}
gpgsql-dbname={{ powerdns_db_name }}
{% elif powerdns_database == 'sqlite3' %}
gsqlite3-database=/var/lib/powerdns/pdns.sqlite3
{% endif %}
# API and web server
api=yes
api-key={{ powerdns_api_key }}
webserver=yes
webserver-address=127.0.0.1
webserver-port=8081
webserver-password={{ powerdns_web_password }}
# DNSSEC
enable-dnssec=yes
# Default SOA settings
default-soa-name={{ powerdns_default_soa_name }}
default-soa-mail={{ powerdns_default_soa_mail }}
# Logging
loglevel=6
logging-facility=0
# Install community DNS role
ansible-galaxy install community.general.pdns
# Or create your own role structure
ansible-galaxy init roles/powerdns
roles/powerdns/
├── defaults/main.yml
├── handlers/main.yml
├── meta/main.yml
├── tasks/
│ ├── main.yml
│ ├── install.yml
│ ├── configure.yml
│ └── setup_db.yml
├── templates/
│ └── pdns.conf.j2
└── vars/main.yml
---
- name: Include installation tasks
import_tasks: install.yml
- name: Include database setup tasks
import_tasks: setup_db.yml
when: powerdns_database != "sqlite3"
- name: Include configuration tasks
import_tasks: configure.yml
- name: Include service management tasks
import_tasks: service.yml
---
# PowerDNS version and components
powerdns_version: "50"
powerdns_components:
- pdns-server
powerdns_backend_package: "pdns-backend-sqlite3"
powerdns_database: "sqlite3"
# Database settings
powerdns_db_host: "localhost"
powerdns_db_user: "pdns"
powerdns_db_password: "changeme"
powerdns_db_name: "pdns"
# Service settings
powerdns_config_dir: "/etc/powerdns"
powerdns_listen_address: "0.0.0.0"
powerdns_port: 53
# API and web settings
powerdns_api_enabled: true
powerdns_api_key: "changeme"
powerdns_webserver_enabled: true
powerdns_webserver_address: "127.0.0.1"
powerdns_webserver_port: 8081
powerdns_webserver_password: "changeme"
# DNS settings
powerdns_enable_dnssec: true
powerdns_default_soa_name: "a.misconfigured.dns.server.example.com"
powerdns_default_soa_mail: "hostmaster@example.com"
---
- name: Deploy complete PowerDNS stack
hosts: powerdns
become: true
vars:
powerdns_version: "50"
powerdns_recursor_version: "50"
powerdns_dnsdist_version: "18"
tasks:
- name: Add PowerDNS repositories (Debian/Ubuntu)
block:
- name: Create keyrings directory
file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
- name: Import PowerDNS GPG key (Key ID: FD380FBB)
ansible.builtin.key:
url: https://repo.powerdns.com/FD380FBB-pub.asc
keyring: /etc/apt/keyrings/pdns-archive-keyring.gpg
state: present
- name: Add PowerDNS Authoritative Server repository
apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/pdns-archive-keyring.gpg] https://repo.powerdns.com/{{ ansible_distribution|lower }}-{{ ansible_distribution_release }}-auth-{{ powerdns_version }}/ {{ ansible_distribution_release }} main"
state: present
filename: pdns-auth
- name: Add PowerDNS Recursor repository
apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/pdns-archive-keyring.gpg] https://repo.powerdns.com/{{ ansible_distribution|lower }}-{{ ansible_distribution_release }}-rec-{{ powerdns_recursor_version }}/ {{ ansible_distribution_release }} main"
state: present
filename: pdns-rec
- name: Add PowerDNS DNSdist repository
apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/pdns-archive-keyring.gpg] https://repo.powerdns.com/{{ ansible_distribution|lower }}-{{ ansible_distribution_release }}-dnsdist-{{ powerdns_dnsdist_version }}/ {{ ansible_distribution_release }} main"
state: present
filename: pdns-dnsdist
- name: Add APT pinning for PowerDNS packages
copy:
dest: /etc/apt/preferences.d/pdns
content: |
Package: pdns-*
Pin: origin repo.powerdns.com
Pin-Priority: 600
mode: "0644"
when: ansible_os_family == "Debian"
- name: Install PowerDNS components
package:
name:
- pdns-server
- pdns-recursor
- dnsdist
state: present
- name: Configure PowerDNS Authoritative Server
import_tasks: configure_authoritative.yml
- name: Configure PowerDNS Recursor
import_tasks: configure_recursor.yml
- name: Configure DNSdist
import_tasks: configure_dnsdist.yml
- name: Enable and start services
systemd:
name: "{{ item }}"
state: started
enabled: true
daemon_reload: yes
loop:
- pdns
- pdns-recursor
- dnsdist
all:
children:
powerdns:
hosts:
pdns1.example.com:
ansible_host: 192.168.1.10
powerdns_database: "postgresql"
powerdns_db_password: "secure_password_1"
powerdns_api_key: "api_key_1"
pdns2.example.com:
ansible_host: 192.168.1.11
powerdns_database: "mysql"
powerdns_db_password: "secure_password_2"
powerdns_api_key: "api_key_2"
# Run the complete playbook
ansible-playbook -i hosts.yml powerdns-playbook.yml
# Run with specific tags
ansible-playbook -i hosts.yml powerdns-playbook.yml --tags "install"
# Run with vault password (if using encrypted variables)
ansible-playbook -i hosts.yml powerdns-playbook.yml --ask-vault-pass
# Dry-run to check what would change
ansible-playbook -i hosts.yml powerdns-playbook.yml --check
Use Ansible Vault for sensitive data:
ansible-vault encrypt_string --name 'powerdns_api_key' 'your_secret_key'
Limit service exposure:
# In your configuration template
webserver-address=127.0.0.1 # Only local access
Use dedicated system user:
# Already configured in the template
setuid=pdns
setgid=pdns
Add monitoring configuration to your playbook:
- name: Install monitoring configuration
copy:
content: |
# PowerDNS metrics for Prometheus
[prometheus]
endpoint=/metrics
allow-from=127.0.0.1,::1
dest: /etc/powerdns/prometheus.conf
owner: pdns
group: pdns
mode: "0640"
notify: restart pdns
Include backup tasks in your playbook:
- name: Install backup script
template:
src: backup_pdns.sh.j2
dest: /usr/local/bin/backup_pdns.sh
mode: "0755"
owner: root
group: root
- name: Install cron job for backups
cron:
name: "PowerDNS backup"
minute: "0"
hour: "2"
job: "/usr/local/bin/backup_pdns.sh"
user: root
Repository access issues:
# Verify GPG key is installed correctly (should show FD380FBB)
gpg --show-keys /etc/apt/keyrings/pdns-archive-keyring.gpg
# Check repository configuration
ansible powerdns -m setup -a "filter=ansible_distribution*"
# Verify repository is accessible
ansible powerdns -a "apt update" --become
Service startup failures:
# Check service status
ansible powerdns -a "systemctl status pdns" --become
# Check logs
ansible powerdns -a "journalctl -u pdns --no-pager" --become
Configuration validation:
# Validate configuration
ansible powerdns -a "pdns_server --config" --become
Database connection issues:
# Test MySQL connectivity
ansible powerdns -a "mysql -u pdns -p -e 'SHOW TABLES;' pdns" --become
# Test PostgreSQL connectivity
ansible powerdns -a "psql -U pdns -d pdns -c '\dt'" --become
We develop tailored automation solutions for:
Let’s discuss your requirements: office@linux-server-admin.com | Contact