PowerDNS Admin Configuration
Edit DB configuration
Input:
cat << EOF > /opt/web/powerdns-admin/powerdnsadmin/default_config.py
import os
from flask_session import Session
from redis import Redis
basedir = os.path.abspath(os.path.dirname(__file__))
# Server settings
BIND_ADDRESS = '0.0.0.0'
PORT = 9191
SECRET_KEY = '6914b035-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# CAPTCHA settings
CAPTCHA_HEIGHT = 60
CAPTCHA_LENGTH = 6
CAPTCHA_WIDTH = 160
CAPTCHA_SESSION_KEY = 'captcha_image'
# Security / cookies
CSRF_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
HSTS_ENABLED = False
# SAML Authnetication
SAML_ENABLED = False
SAML_ASSERTION_ENCRYPTED = True
# Redis session
SESSION_TYPE = 'redis'
SESSION_REDIS = Redis.from_url("redis://localhost:6379")
SESSION_PERMANENT = False
# Database (MySQL)
SQLA_DB_USER = 'powerdns'
SQLA_DB_PASSWORD = 'dbpassword'
SQLA_DB_HOST = 'localhost'
SQLA_DB_NAME = 'powerdns'
SQLALCHEMY_DATABASE_URI = 'mysql://'+SQLA_DB_USER+':'+SQLA_DB_PASSWORD+'@'+SQLA_DB_HOST+'/'+SQLA_DB_NAME
SQLALCHEMY_TRACK_MODIFICATIONS = False
### SMTP config
#MAIL_SERVER =
#MAIL_PORT =
#MAIL_DEBUG =
#MAIL_USE_TLS =
#MAIL_USE_SSL =
#MAIL_USERNAME =
#MAIL_PASSWORD =
#MAIL_DEFAULT_SENDER =
EOF
Prepare DB migrations
Input:
export FLASK_APP=powerdnsadmin/__init__.py
flask db init
flask db migrate -m "Init DB"
Build assets
Input:
npm install -g yarn@1
/usr/local/bin/yarn install --pure-lockfile
flask assets build
Create a new group for PowerDNS-Admin
groupadd powerdnsadmin
Create a user for PowerDNS-Admin:
useradd --system -g powerdnsadmin powerdnsadmin
--system creates a user without login-shell and password, suitable for running system services.Create runtime directory on boot
Input:
cat << 'EOF' > /etc/tmpfiles.d/powerdns-admin.conf
d /run/powerdns-admin 0770 powerdnsadmin www-data -
EOF
This creates /run/powerdns-admin at boot owned by user powerdnsadmin and group www-data.
Create Gunicorn service
Input:
cat << 'EOF' > /etc/systemd/system/powerdns-admin.service
[Unit]
Description=PowerDNS-Admin
Requires=powerdns-admin.socket
After=network.target
[Service]
User=powerdnsadmin
Group=powerdnsadmin
WorkingDirectory=/opt/web/powerdns-admin
PIDFile=/run/powerdns-admin/pid
Environment=TMPDIR=/tmp
ExecStart=/opt/web/powerdns-admin/pdns/bin/gunicorn \
--pid /run/powerdns-admin/pid \
--bind unix:/run/powerdns-admin/socket \
"powerdnsadmin:create_app()"
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
Gunicorn runs as user powerdnsadmin for correct ownership and security.
Create socket unit
Input:
cat << 'EOF' > /etc/systemd/system/powerdns-admin.socket
[Unit]
Description=PowerDNS-Admin socket
[Socket]
ListenStream=/run/powerdns-admin/socket
SocketUser=powerdnsadmin
SocketGroup=www-data
SocketMode=0660
[Install]
WantedBy=sockets.target
EOF
The socket is readable by powerdnsadmin and www-data, allowing Nginx to connect.
Fix directory ownership and permissions
Input:
chown -R powerdnsadmin:powerdnsadmin /opt/web/powerdns-admin
chown -R powerdnsadmin:powerdnsadmin /run/powerdns-admin
chmod 770 /run/powerdns-admin
Fix PID file path (directory must already exist)
Input:
install -d -m 770 -o powerdnsadmin -g powerdnsadmin /run/powerdns-admin
Reload systemd and enable services
Input:
systemctl daemon-reload
systemctl enable --now redis-server
systemctl enable powerdns-admin.socket
systemctl restart powerdns-admin.socket
systemctl restart powerdns-admin.service
systemctl restart nginx
Verify services
Input:
systemctl status powerdns-admin.service
Output:
● powerdns-admin.service - PowerDNS-Admin
Loaded: loaded (/etc/systemd/system/powerdns-admin.service; disabled; preset: enabled)
Active: active (running) since Mon 2025-12-08 04:45:18 UTC; 1h 39min ago
TriggeredBy: ● powerdns-admin.socket
Main PID: 50305 (gunicorn)
Tasks: 2 (limit: 1030)
Memory: 104.0M
CPU: 1.768s
CGroup: /system.slice/powerdns-admin.service
├─50305 /opt/web/powerdns-admin/pdns/bin/python3 /opt/web/powerdns-admin/pdns/bin/gunicorn --pid /run/powerdns-admin/pid --bind unix:/run/powerdns-admin/so>
└─50308 /opt/web/powerdns-admin/pdns/bin/python3 /opt/web/powerdns-admin/pdns/bin/gunicorn --pid /run/powerdns-admin/pid --bind unix:/run/powerdns-admin/so>
Dec 08 04:45:18 maste-pdns systemd[1]: Started powerdns-admin.service - PowerDNS-Admin.
Dec 08 04:45:19 maste-pdns gunicorn[50305]: [2025-12-08 04:45:19 +0000] [50305] [INFO] Starting gunicorn 20.1.0
Dec 08 04:45:19 maste-pdns gunicorn[50305]: [2025-12-08 04:45:19 +0000] [50305] [INFO] Listening at: unix:/run/powerdns-admin/socket (50305)
Dec 08 04:45:19 maste-pdns gunicorn[50305]: [2025-12-08 04:45:19 +0000] [50305] [INFO] Using worker: sync
Dec 08 04:45:19 maste-pdns gunicorn[50308]: [2025-12-08 04:45:19 +0000] [50308] [INFO] Booting worker with pid: 50308
Verify Socket
Input:
systemctl status powerdns-admin.socket
Output:
● powerdns-admin.socket - PowerDNS-Admin socket
Loaded: loaded (/etc/systemd/system/powerdns-admin.socket; enabled; preset: enabled)
Active: active (running) since Mon 2025-12-08 04:20:18 UTC; 2h 4min ago
Triggers: ● powerdns-admin.service
Listen: /run/powerdns-admin/socket (Stream)
Tasks: 0 (limit: 1030)
Memory: 0B
CPU: 575us
CGroup: /system.slice/powerdns-admin.socket
Dec 08 04:20:18 maste-pdns systemd[1]: Starting powerdns-admin.socket - PowerDNS-Admin socket...
Dec 08 04:20:18 maste-pdns systemd[1]: Listening on powerdns-admin.socket - PowerDNS-Admin socket.
This confirms Gunicorn and its socket are active and ready.