This guide installs Poweradmin with Apache/Nginx, PHP, MariaDB/PostgreSQL dependencies, and optionally deploys PowerDNS backend for complete DNS management automation.
Note: Ubuntu uses the Debian-family playbook with minor adjustments.
Create inventory.ini:
[poweradmin]
dns-admin.example.com ansible_host=192.168.1.100
[poweradmin:vars]
# Web server choice: apache or nginx
web_server=apache
# Database backend: mysql, postgresql, or sqlite
db_backend=mysql
# PowerDNS integration
enable_powerdns=true
powerdns_backend=mysql
# Domain settings
server_domain=dns-admin.example.com
admin_email=admin@example.com
Create poweradmin-install.yml:
---
- name: Install and configure Poweradmin
hosts: poweradmin
become: true
vars:
# Poweradmin settings
poweradmin_version: "v4.0.7"
poweradmin_web_root: /var/www/poweradmin
poweradmin_repo: "https://github.com/poweradmin/poweradmin.git"
poweradmin_domain: "{{ server_domain | default('dns-admin.example.com') }}"
# Database settings
db_backend: mysql # mysql, postgresql, or sqlite
poweradmin_db_name: poweradmin
poweradmin_db_user: poweradmin
poweradmin_db_password: "{{ vault_poweradmin_db_password | default('change_me_poweradmin_db') }}"
# PowerDNS database settings
pdns_db_name: pdns
pdns_db_user: pdns
pdns_db_password: "{{ vault_pdns_db_password | default('change_me_pdns_db') }}"
# PowerDNS API settings
pdns_api_key: "{{ vault_pdns_api_key | default('change_me_api_key') }}"
pdns_api_port: 8081
# Web server settings
web_server: apache # apache or nginx
ssl_enabled: false
# Security settings
firewall_enabled: true
fail2ban_enabled: true
handlers:
- name: restart apache
systemd:
name: apache2
state: restarted
when: web_server == 'apache'
- name: restart nginx
systemd:
name: nginx
state: restarted
when: web_server == 'nginx'
- name: restart php-fpm
systemd:
name: "php{{ php_version }}-fpm"
state: restarted
- name: restart mysql
systemd:
name: mysql
state: restarted
- name: restart mariadb
systemd:
name: mariadb
state: restarted
- name: restart postgresql
systemd:
name: postgresql
state: restarted
- name: restart pdns
systemd:
name: pdns
state: restarted
tasks:
# =========================================
# Prerequisites
# =========================================
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Install prerequisites (Debian/Ubuntu)
apt:
name:
- software-properties-common
- gnupg2
- curl
- git
- unzip
state: present
when: ansible_os_family == "Debian"
- name: Install prerequisites (RHEL)
dnf:
name:
- dnf-utils
- gnupg2
- curl
- git
- unzip
state: present
when: ansible_os_family == "RedHat"
# =========================================
# Install PHP
# =========================================
- name: Add PHP repository (Ubuntu)
apt_repository:
repo: "ppa:ondrej/php"
state: present
when:
- ansible_os_family == "Debian"
- ansible_distribution == "Ubuntu"
- name: Install PHP and extensions (Debian/Ubuntu)
apt:
name:
- php{{ php_version | default('8.2') }}
- php{{ php_version | default('8.2') }}-intl
- php{{ php_version | default('8.2') }}-gd
- php{{ php_version | default('8.2') }}-mysql
- php{{ php_version | default('8.2') }}-pgsql
- php{{ php_version | default('8.2') }}-curl
- php{{ php_version | default('8.2') }}-xml
- php{{ php_version | default('8.2') }}-mbstring
- php{{ php_version | default('8.2') }}-zip
- php{{ php_version | default('8.2') }}-bcmath
- libapache2-mod-php{{ php_version | default('8.2') }}
state: present
when:
- ansible_os_family == "Debian"
- web_server == 'apache'
notify: restart apache
- name: Install PHP-FPM (Debian/Ubuntu with Nginx)
apt:
name:
- php{{ php_version | default('8.2') }}-fpm
state: present
when:
- ansible_os_family == "Debian"
- web_server == 'nginx'
notify: restart php-fpm
- name: Install PHP and extensions (RHEL)
dnf:
name:
- php
- php-intl
- php-gd
- php-mysqlnd
- php-pgsql
- php-process
- php-xml
- php-mbstring
- php-zip
state: present
when: ansible_os_family == "RedHat"
# =========================================
# Install Database Server
# =========================================
- name: Install MariaDB (Debian/Ubuntu)
apt:
name:
- mariadb-server
- mariadb-client
- python3-mysqldb
state: present
when:
- ansible_os_family == "Debian"
- db_backend == 'mysql'
notify: restart mariadb
- name: Install MySQL (RHEL)
dnf:
name:
- mariadb-server
- mariadb
- python3-PyMySQL
state: present
when:
- ansible_os_family == "RedHat"
- db_backend == 'mysql'
notify: restart mariadb
- name: Install PostgreSQL (Debian/Ubuntu)
apt:
name:
- postgresql
- postgresql-contrib
- libpq-dev
- python3-psycopg2
state: present
when:
- ansible_os_family == "Debian"
- db_backend == 'postgresql'
notify: restart postgresql
- name: Enable and start database service
systemd:
name: "{{ 'mariadb' if db_backend == 'mysql' else 'postgresql' }}"
enabled: true
state: started
# =========================================
# Install Web Server
# =========================================
- name: Install Apache (Debian/Ubuntu)
apt:
name:
- apache2
- libapache2-mod-php{{ php_version | default('8.2') }}
state: present
when:
- ansible_os_family == "Debian"
- web_server == 'apache'
notify: restart apache
- name: Install Nginx (Debian/Ubuntu)
apt:
name:
- nginx
state: present
when:
- ansible_os_family == "Debian"
- web_server == 'nginx'
notify: restart nginx
- name: Install Apache (RHEL)
dnf:
name:
- httpd
- mod_php
state: present
when:
- ansible_os_family == "RedHat"
- web_server == 'apache'
notify: restart apache
- name: Enable and start web server
systemd:
name: "{{ 'apache2' if (ansible_os_family == 'Debian' and web_server == 'apache') else ('nginx' if web_server == 'nginx' else 'httpd') }}"
enabled: true
state: started
# =========================================
# Deploy Poweradmin
# =========================================
- name: Create Poweradmin directory
file:
path: "{{ poweradmin_web_root }}"
state: directory
mode: '0755'
- name: Clone Poweradmin repository
git:
repo: "{{ poweradmin_repo }}"
dest: "{{ poweradmin_web_root }}"
version: "{{ poweradmin_version }}"
force: false
- name: Set ownership for web root
file:
path: "{{ poweradmin_web_root }}"
state: directory
recurse: true
owner: "{{ 'www-data' if ansible_os_family == 'Debian' else 'apache' }}"
group: "{{ 'www-data' if ansible_os_family == 'Debian' else 'apache' }}"
- name: Set permissions for var directory
file:
path: "{{ poweradmin_web_root }}/var"
state: directory
recurse: true
mode: '0775'
# =========================================
# Configure Database
# =========================================
- name: Create Poweradmin database (MySQL)
mysql_db:
name: "{{ poweradmin_db_name }}"
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
when: db_backend == 'mysql'
- name: Create Poweradmin database user (MySQL)
mysql_user:
name: "{{ poweradmin_db_user }}"
password: "{{ poweradmin_db_password }}"
priv: "{{ poweradmin_db_name }}.*:ALL"
host: localhost
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
when: db_backend == 'mysql'
- name: Create PowerDNS database (MySQL)
mysql_db:
name: "{{ pdns_db_name }}"
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
when:
- enable_powerdns | default(false)
- db_backend == 'mysql'
- name: Create PowerDNS database user (MySQL)
mysql_user:
name: "{{ pdns_db_user }}"
password: "{{ pdns_db_password }}"
priv: "{{ pdns_db_name }}.*:ALL"
host: localhost
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
when:
- enable_powerdns | default(false)
- db_backend == 'mysql'
- name: Download PowerDNS schema
get_url:
url: "https://raw.githubusercontent.com/PowerDNS/pdns/master/modules/gmysqlbackend/schema.mysql.sql"
dest: /tmp/pdns_schema.sql
mode: '0644'
when:
- enable_powerdns | default(false)
- db_backend == 'mysql'
- name: Import PowerDNS schema
mysql_db:
name: "{{ pdns_db_name }}"
state: import
target: /tmp/pdns_schema.sql
login_unix_socket: /var/run/mysqld/mysqld.sock
when:
- enable_powerdns | default(false)
- db_backend == 'mysql'
- name: Clean up temporary schema file
file:
path: /tmp/pdns_schema.sql
state: absent
when:
- enable_powerdns | default(false)
- db_backend == 'mysql'
# =========================================
# Configure Web Server
# =========================================
- name: Create Apache virtual host
template:
src: apache_vhost.conf.j2
dest: "/etc/apache2/sites-available/{{ poweradmin_domain }}.conf"
mode: '0644'
when:
- ansible_os_family == "Debian"
- web_server == 'apache'
notify: restart apache
- name: Enable Apache site
command: "a2ensite {{ poweradmin_domain }}.conf"
args:
creates: "/etc/apache2/sites-enabled/{{ poweradmin_domain }}.conf"
when:
- ansible_os_family == "Debian"
- web_server == 'apache'
notify: restart apache
- name: Enable Apache modules
apache2_module:
name: "{{ item }}"
state: present
loop:
- rewrite
- headers
- ssl
when:
- ansible_os_family == "Debian"
- web_server == 'apache'
notify: restart apache
- name: Create Nginx server block
template:
src: nginx_server.conf.j2
dest: "/etc/nginx/sites-available/{{ poweradmin_domain }}"
mode: '0644'
when:
- ansible_os_family == "Debian"
- web_server == 'nginx'
notify: restart nginx
- name: Enable Nginx site
file:
src: "/etc/nginx/sites-available/{{ poweradmin_domain }}"
dest: "/etc/nginx/sites-enabled/{{ poweradmin_domain }}"
state: link
when:
- ansible_os_family == "Debian"
- web_server == 'nginx'
notify: restart nginx
# =========================================
# Install PowerDNS (Optional)
# =========================================
- name: Add PowerDNS repository key (Debian/Ubuntu)
apt_key:
url: https://repo.powerdns.com/FD380FBB-pub.asc
state: present
when:
- ansible_os_family == "Debian"
- enable_powerdns | default(false)
- name: Add PowerDNS repository (Debian/Ubuntu)
apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/pdns-archive-keyring.gpg] https://repo.powerdns.com/{{ ansible_distribution|lower }}-{{ ansible_distribution_release }}-auth-50/ {{ ansible_distribution_release }} main"
state: present
filename: pdns-auth
when:
- ansible_os_family == "Debian"
- enable_powerdns | default(false)
- name: Install PowerDNS (Debian/Ubuntu)
apt:
name:
- pdns-server
- pdns-backend-mysql
state: present
when:
- ansible_os_family == "Debian"
- enable_powerdns | default(false)
- db_backend == 'mysql'
notify: restart pdns
- name: Configure PowerDNS
template:
src: pdns.conf.j2
dest: /etc/powerdns/pdns.conf
mode: '0640'
owner: pdns
group: pdns
when: enable_powerdns | default(false)
notify: restart pdns
- name: Enable and start PowerDNS
systemd:
name: pdns
enabled: true
state: started
when: enable_powerdns | default(false)
# =========================================
# Security Hardening
# =========================================
- name: Install fail2ban
apt:
name: fail2ban
state: present
when:
- ansible_os_family == "Debian"
- fail2ban_enabled | default(true)
- name: Configure UFW firewall
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "80"
- "443"
when:
- ansible_os_family == "Debian"
- firewall_enabled | default(true)
# =========================================
# Final Configuration
# =========================================
- name: Create Poweradmin config file
template:
src: config.inc.php.j2
dest: "{{ poweradmin_web_root }}/config.inc.php"
mode: '0640'
owner: "{{ 'www-data' if ansible_os_family == 'Debian' else 'apache' }}"
group: "{{ 'www-data' if ansible_os_family == 'Debian' else 'apache' }}"
- name: Display completion message
debug:
msg: |
Poweradmin installation completed!
Access URL: http://{{ poweradmin_domain }}
Default admin: admin / admin (CHANGE IMMEDIATELY!)
PowerDNS API: http://{{ ansible_host }}:{{ pdns_api_port }}
PowerDNS API Key: {{ pdns_api_key }}
<VirtualHost *:80>
ServerName {{ poweradmin_domain }}
DocumentRoot {{ poweradmin_web_root }}/web
<Directory {{ poweradmin_web_root }}/web>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
</Directory>
# Security headers
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
ErrorLog ${APACHE_LOG_DIR}/poweradmin_error.log
CustomLog ${APACHE_LOG_DIR}/poweradmin_access.log combined
</VirtualHost>
server {
listen 80;
server_name {{ poweradmin_domain }};
root {{ poweradmin_web_root }}/web;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php{{ php_version | default('8.2') }}-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
# Security headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
access_log /var/log/nginx/poweradmin_access.log;
error_log /var/log/nginx/poweradmin_error.log;
}
# PowerDNS Configuration - Managed by Ansible
local-address=0.0.0.0
local-port=53
daemon=yes
setuid=pdns
setgid=pdns
# Backend
launch=gmysql
gmysql-host=localhost
gmysql-user={{ pdns_db_user }}
gmysql-password={{ pdns_db_password }}
gmysql-dbname={{ pdns_db_name }}
# API Configuration
api=yes
api-key={{ pdns_api_key }}
webserver=yes
webserver-address=127.0.0.1
webserver-port={{ pdns_api_port }}
webserver-password={{ pdns_api_key }}
# DNSSEC
enable-dnssec=yes
# Logging
loglevel=6
<?php
// Poweradmin Configuration - Managed by Ansible
// Poweradmin Database
$poweradmin_db_host = 'localhost';
$poweradmin_db_port = '3306';
$poweradmin_db_name = '{{ poweradmin_db_name }}';
$poweradmin_db_user = '{{ poweradmin_db_user }}';
$poweradmin_db_pass = '{{ poweradmin_db_password }}';
$poweradmin_db_type = 'mysql';
// PowerDNS Database
$pdns_db_host = 'localhost';
$pdns_db_port = '3306';
$pdns_db_name = '{{ pdns_db_name }}';
$pdns_db_user = '{{ pdns_db_user }}';
$pdns_db_pass = '{{ pdns_db_password }}';
$pdns_db_type = 'mysql';
// PowerDNS API
$api_access_key = '{{ pdns_api_key }}';
$api_protocol = 'http';
$api_host = '127.0.0.1';
$api_port = '{{ pdns_api_port }}';
// Security
$session_timeout = 3600;
$password_hash_algo = PASSWORD_DEFAULT;
?>
# Basic installation
ansible-playbook -i inventory.ini poweradmin-install.yml
# With vault for sensitive data
ansible-playbook -i inventory.ini poweradmin-install.yml --ask-vault-pass
# Dry run (check mode)
ansible-playbook -i inventory.ini poweradmin-install.yml --check
# Limit to specific tasks
ansible-playbook -i inventory.ini poweradmin-install.yml --tags "database,webserver"
# Verbose output
ansible-playbook -i inventory.ini poweradmin-install.yml -vv
Create encrypted vault file:
# Create vault file
ansible-vault create group_vars/poweradmin/vault.yml
# Add sensitive variables
cat >> group_vars/poweradmin/vault.yml << EOF
vault_poweradmin_db_password: "your_secure_poweradmin_password"
vault_pdns_db_password: "your_secure_pdns_password"
vault_pdns_api_key: "your_secure_api_key"
EOF
# Run with vault
ansible-playbook -i inventory.ini poweradmin-install.yml --ask-vault-pass
poweradmin_debian inventory groupcommunity.mysql collection before running DB tasks:ansible-galaxy collection install community.mysql
http://dns-admin.example.comadmin/admin and change immediatelyNeed custom Ansible playbooks for your DNS infrastructure? We develop tailored automation solutions for Poweradmin, PowerDNS, and complex multi-server deployments. Get in touch at office@linux-server-admin.com or through our contact page.