Get up and running on with your own DNS and DHCP server from scratch (PowerDNS + isc-dhcp-server)



Part 1. Install Required Packages

# Ensure we are running the latest version of Debian and all packages are up to date. 
apt update && apt upgrade -y && apt dist-upgrade -y
# Install MariaDB for the PowerDNS backend
apt install mariadb-server -y
# Install PowerDNS with MySQL support for the backend
apt-get install pdns-server pdns-backend-mysql pdns-tools dnsutils -y
# By default PowerDNS installs support for bind. We can remove this as we are using MySQL
apt remove pdns-backend-bind -y
# Install isc-dhcp-server
apt install isc-dhcp-server -y
# Note: Ignore the errors during installation
# Install a firewall to restrict access and unattended-upgrades to for automatic security patching.
apt install ufw unattended-upgrades -y
# Next lets stop both services while we are configuring them
service isc-dhcp-server stop
service pdns stop

Step 2. Setup MariaDB for PowerDNS

root@dns1:~# mysql_secure_installationNOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
In order to log into MariaDB to secure it, we’ll need the current
password for the root user. If you’ve just installed MariaDB, and
you haven’t set the root password yet, the password will be blank,
so you should just press enter here.
Enter current password for root (enter for none):
OK, successfully used password, moving on…
Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.
Set root password? [Y/n] y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
… Success!
By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them. This is intended only for testing, and to make the installation
go a bit smoother. You should remove them before moving into a
production environment.
Remove anonymous users? [Y/n] y
… Success!
Normally, root should only be allowed to connect from ‘localhost’. This
ensures that someone cannot guess at the root password from the network.
Disallow root login remotely? [Y/n] y
… Success!
By default, MariaDB comes with a database named ‘test’ that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.
Remove test database and access to it? [Y/n] y
— Dropping test database…
… Success!
— Removing privileges on test database…
… Success!
Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.
Reload privilege tables now? [Y/n] y
… Success!
Cleaning up…All done! If you’ve completed all of the above steps, your MariaDB
installation should now be secure.
Thanks for using MariaDB!
# Connect to MariaDB 
root@dns1:~# mysql -u root -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
# Create the database
Query OK, 1 row affected (0.000 sec)
# Create a user called pdns that connects from the localhost using the password specified below (change the password for your case)
GRANT ALL ON pdns.* TO 'pdns'@'localhost' IDENTIFIED BY 'ReallyStrongPassword';
Query OK, 0 rows affected (0.000 sec)
# Flush the privileges and quit
Query OK, 0 rows affected (0.000 sec)

Part 3. Database Schema

root@dns1:~# apt search pdns-server
Sorting… Done
Full Text Search… Done
pdns-server/stable,now 4.1.6–3 amd64 [installed]
extremely powerful and versatile nameserver
# Use the newly created database
USE pdns;

Part 4. Configure the mysql backend for PowerDNS

# Remove bind.conf as we are using mysql as our bandend. 
rm /etc/powerdns/pdns.d/bind.conf
# Create a mysql config file and specify your sql settings
nano /etc/powerdns/pdns.d/pdns.local.gmysql.conf
# Add the following lines to the MySQL Configuration file
gmysql-password=ReallyStrongPassword #<< Change to your own pass
root@dns1:/# service pdns restart
root@dns1:/# service pdns status
● pdns.service — PowerDNS Authoritative Server
Loaded: loaded (/lib/systemd/system/pdns.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2020–06–14 12:18:37 JST; 30s ago
Docs: man:pdns_server(1)
Main PID: 5651 (pdns_server)
Tasks: 8 (limit: 4700)
Memory: 4.8M
CGroup: /system.slice/pdns.service
└─5651 /usr/sbin/pdns_server — guardian=no — daemon=no — disable-syslog — log-timestamp=no — write-pid=no
Jun 14 12:18:37 dns1 pdns_server[5651]: UDPv6 server bound to [::]:53
Jun 14 12:18:37 dns1 pdns_server[5651]: TCP server bound to
Jun 14 12:18:37 dns1 pdns_server[5651]: TCPv6 server bound to [::]:53
Jun 14 12:18:37 dns1 pdns_server[5651]: PowerDNS Authoritative Server 4.1.6 © 2001–2018 PowerDNS.COM BV
Jun 14 12:18:37 dns1 pdns_server[5651]: Using 64-bits mode. Built using gcc 8.3.0.
Jun 14 12:18:37 dns1 pdns_server[5651]: PowerDNS comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribu
Jun 14 12:18:37 dns1 pdns_server[5651]: Creating backend connection for TCP
Jun 14 12:18:37 dns1 pdns_server[5651]: About to create 3 backend threads for UDP
Jun 14 12:18:37 dns1 systemd[1]: Started PowerDNS Authoritative Server.
Jun 14 12:18:37 dns1 pdns_server[5651]: Done launching threads, ready to distribute questions
# Edit your bash_completion and make sure the following line is added:
nano ~/.bash_completion
for bcfile in ~/.bash_completion.d/* ; do
. $bcfile
# Make a user level bash_completion directory
mkdir ~/.bash_completion.d/
# Download the bash_completion file provided by PowerDNS
cd ~/.bash_completion.d/

Step 5. Zone Creation

# Create your first zone with PowerDNS
root@dns1:~# pdnsutil create-zone
Creating empty zone ‘’
pdnsutil edit-zone {zone name}
(a)pply these changes, (e)dit again, (r)etry with original zone, (q)uit: e
Checked 3 records of ‘’, 0 errors, 0 warnings.
Detected the following changes: 3600 IN NS 3600 IN SOA a.misconfigured.powerdns.server 1 10800 3600 604800 3600 3600 IN SOA 1 10800 3600 604800 3600 3600 IN A
(a)pply these changes, (e)dit again, (r)etry with original zone, (q)uit: a
Adding empty non-terminals for non-DNSSEC zone
root@dns1:~# dig @localhost; <<>> DiG 9.11.5-P4–5.1+deb10u1-Debian <<>> @localhost
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51625
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
; EDNS: version: 0, flags:; udp: 1680
; IN A
;; AUTHORITY SECTION: 3600 IN SOA 1 10800 3600 604800 3600
;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Sun Jun 14 14:27:46 JST 2020
;; MSG SIZE rcvd: 100

Step 6. Creating a TSIG key

# Create a key called 'dhcp-key' using hmac-sha256
root@dns1:~# pdnsutil generate-tsig-key dhcp-key hmac-sha256
Generating new key with 64 bytes
Create new TSIG key dhcp-key hmac-sha256 eNgfPSPxyN3xX4Z91Ba+UfQaBXYa7l67Nw/F4iuKOEnOllI+A1ZB5MK8qpemjSThO3riCtnJc01HPMKDFMqADA==
# Enable the key on your DNS zone
root@dns1:~# pdnsutil activate-tsig-key dhcp-key master
Enabled TSIG key dhcp-key for
# To verify you can list your keys
root@dns1:~# pdnsutil list-tsig-keys
dhcp-key. hmac-sha256. eNgfPSPxyN3xX4Z91Ba+UfQaBXYa7l67Nw/F4iuKOEnOllI+A1ZB5MK8qpemjSThO3riCtnJc01HPMKDFMqADA==
# Verify the key is enabled for your domain
root@dns1:~# pdnsutil show-zone
This is a Native zone
Zone is not actively secured
Zone has following allowed TSIG key(s): dhcp-key
Metadata items:
No keys for zone ''.

Step 7. Enable DNS updates

root@dns1:/etc/dhcp# nano /etc/powerdns/pdns.conf#Allow dnsupdates from localhost
# Enable DNS updates

8. Setup isc-dhcp-server

root@dns1:~# ip a
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether ca:fe:42:9d:1f:1d brd ff:ff:ff:ff:ff:ff
inet brd scope global dynamic ens18
root@dns1:/etc/dhcp# service isc-dhcp-server stop
# Edit the following file
root@dns1:~# nano /etc/default/isc-dhcp-server
# Add the interface name to INTERFACESv4
# Move the original dhcpd.conf (Useful as a future reference)
mv /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.backup
nano /etc/dhcp/dhcpd.conf
default-lease-time 720000;
max-lease-time 2160000;
ping-check true;
update-static-leases on;

ddns-updates on;
ddns-update-style standard;
ddns-ttl 300;
ddns-domainname "";

# Add the TSIG key generated with pdnsutil
key "dhcp-key" {
algorithm hmac-sha256;
secret "eNgfPSPxyN3xX4Z91Ba+UfQaBXYa7l67Nw/F4iuKOEnOllI+A1ZB5MK8qpemjSThO3riCtnJc01HPMKDFMqADA==";

# Specify the zone name and the DNS server on the localhost
zone {
key dhcp-key;
# Internal Network Scope
subnet netmask {
option domain-name-servers,;
option domain-search "";
option routers;

pool {
# Download the latest OUI list
wget -O /usr/local/etc/oui.txt
# included with isc-dhcp-server is a command that shows your dhcp clients, and the manufacturer of their MAC addresses. root@dns1:~# dhcp-lease-list
Reading leases from /var/lib/dhcp/dhcpd.leases
MAC IP hostname manufacturer
00:0c:1.. 10.2. ups1 CyberPower Systems, Inc.
00:0c:1.. 10.2. ups2 CyberPower Systems, Inc.
18:66:d.. 10.2. idrac-srv1 Dell Inc.
root@dns1:~# service isc-dhcp-server start
root@dns1:~# service isc-dhcp-server status
● isc-dhcp-server.service — LSB: DHCP server
Loaded: loaded (/etc/init.d/isc-dhcp-server; generated)
Active: active (running) since Mon 2020–06–15 11:31:36 JST; 3s ago
Docs: man:systemd-sysv-generator(8)
Process: 10794 ExecStart=/etc/init.d/isc-dhcp-server start (code=exited, status=0/SUCCESS)
Tasks: 1 (limit: 4700)
Memory: 12.0M
CGroup: /system.slice/isc-dhcp-server.service
└─10806 /usr/sbin/dhcpd -4 -q -cf /etc/dhcp/dhcpd.conf ens18
Jun 15 11:31:34 dns1 systemd[1]: Starting LSB: DHCP server…
Jun 15 11:31:34 dns1 isc-dhcp-server[10794]: Launching IPv4 server only.
Jun 15 11:31:34 dns1 dhcpd[10806]: Wrote 2 leases to leases file.
Jun 15 11:31:34 dns1 dhcpd[10806]: Server starting service.
Jun 15 11:31:36 dns1 isc-dhcp-server[10794]: Starting ISC DHCPv4 server: dhcpd.
Jun 15 11:31:36 dns1 systemd[1]: Started LSB: DHCP server.
# We can also verify the DNS record is updated from the syslog
root@dns1:/etc/dhcp# grep dhcpd /var/log/syslog
dns1 dhcpd[8861]: DHCPRELEASE of from ca:fe:ff:81:4c:33 (test2) via ens18 (found)
dns1 dhcpd[8861]: DHCPDISCOVER from ca:fe:ff:81:4c:33 via ens18
dns1 dhcpd[8861]: DHCPOFFER on to ca:fe:ff:81:4c:33 (test2) via ens18
dns1 dhcpd[8861]: DHCPREQUEST for ( from ca:fe:ff:81:4c:33 (test2) via ens18
dns1 dhcpd[8861]: DHCPACK on to ca:fe:ff:81:4c:33 (test2) via ens18
dns1 dhcpd[8861]: Added new forward map from to
# pdnsutil list-zone will start to show records for dhcp clients.
root@dns1:/etc/dhcp# pdnsutil list-zone
$ORIGIN . 3600 IN A 3600 IN NS 3600 IN SOA 2020061501 10800 3600 6048
00 3600 300 IN A 300 IN DHCID AAIBhkP+9vwMjeINBpbOj+uAI3l7fClXxGksBGRsmSror6Y=

9. Firewall

dns1:~# ufw allow dns
dns1:~# ufw allow ssh
dns1:~# ufw allow 67/udp comment “allow isc-dhcp-server respones”
dns1:~# ufw enable
dns1:~# ufw status
Status: active

To Action From
-- ------ ----
DNS ALLOW Anywhere
67/udp ALLOW Anywhere
22/tcp ALLOW Anywhere
DNS (v6) ALLOW Anywhere (v6)
67/udp (v6) ALLOW Anywhere (v6)
22/tcp (v6) ALLOW Anywhere (v6)

10. Additional Considerations

master / slave replication
Example of running both pdns-server and pdns-recursor on the same PC
pihole settings page to allow you to specify a conditional forwarder




Cloud and Infrastructure Engineer

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

What is the Best Way to Learn Java?

Options for Kafka to SAP ERP Integration and Connector

⚡️FlashChat : Jetpack Compose + Firebase

Elasticity Task

Let’s build a iPhone messaging app with Swift

How I Went From Sociology to Coding in a Year

AWSPremiumSupport-ExtendVolumesOnLinux and AWSPremiumSupport-ExtendVolumesOnWindows” Automation…

Storage Elasticity to Hadoop DateNode using LVM | Resize Static Partition without data loss

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Carl Liebich

Carl Liebich

Cloud and Infrastructure Engineer

More from Medium

Podman Installation on Windows

How-To: Open Terminal Tabs & Execute Commands via your CLI

SOC147 — SSH Scan activity

Linux distribution: deepin 20.6 version planning