Real RBAC
Five system roles plus org-defined custom roles. ~60 actions across zones, records, DNSSEC, TSIG, users, and tokens - scoped to global, team, zone, or server.
A modern, self-hosted admin UI for PowerDNS Authoritative - first-class RBAC, OIDC single sign-on, multi-backend management, diff-before-apply zone editing, and an append-only audit log. One Docker image. No telemetry. No CDN.
Designed for teams that actually operate multi-backend infrastructure.
Five system roles plus org-defined custom roles. ~60 actions across zones, records, DNSSEC, TSIG, users, and tokens - scoped to global, team, zone, or server.
Local accounts with Argon2id, generic OIDC with PKCE and group→role mapping, TOTP MFA, and scoped pda_pat_ API tokens. RP-initiated logout signs out at the IdP.
One install fronts standalone primaries, primary + secondary groups, and multi-primary clusters. Zones merge into one searchable, amalgamated list.
A per-RRset editor with per-type validators and optimistic concurrency. Every change is previewed as a BIND-style before / after diff before it's written.
Cryptokey create / update / delete with per-key activity from the audit log. Manage TSIG keys and autoprimary registrations, with reveal split into its own permission.
Every write logged with redacted before/after snapshots, a per-row PDNS HTTP trail, per-zone history, and operator-driven CSV export.
NOTIFY-aware sync probes compare every secondary's serial against the primary, record-for-record on demand - the same UI shape for replicated clusters.
Pino structured logs (secret-redacted), Prometheus /metrics, /healthz liveness, and /readyz readiness gated on DB + migration version.
A single provisioning.yaml applied on first boot brings up settings, roles, teams, templates, backends, demo zones, and OIDC providers - zero clicks.
Every page reflows cleanly down to a phone viewport. Toggle the theme above - the shots follow.
Ships as a single image - ghcr.io/powerdns-authadmin/powerdns-authadmin. Runs on SQLite or Postgres; migrations and the role seed run automatically on boot.
# Minimal production stack - SQLite, single instance
services:
app:
image: ghcr.io/powerdns-authadmin/powerdns-authadmin:latest
restart: unless-stopped
ports: ["3000:3000"]
environment:
# Exact public URL the browser uses - scheme + host must match
APP_URL: https://dns.example.com
DATABASE_URL: file:/data/powerdns_authadmin.db
# Generate once into .env, back up, never change them
APP_SECRET_KEY: ${APP_SECRET_KEY}
APP_ENCRYPTION_KEY: ${APP_ENCRYPTION_KEY}
# First admin account, created on first boot
BOOTSTRAP_ADMIN_EMAIL: admin@example.com
BOOTSTRAP_ADMIN_PASSWORD: ${BOOTSTRAP_ADMIN_PASSWORD}
# Optional: replication-aware sync UI (default false)
PDNS_BACKGROUND_POLLING: "false"
# Optional: apply provisioning.yaml once on first boot
PROVISION_ON_BOOT: "true"
PROVISIONING_FILE: /etc/pda/provisioning.yaml
# ── Outbound-URL guards (opt-in, off by default) ──
# Enable when PowerDNS / your IdP are on a private
# network or speak http:// (e.g. in-compose PowerDNS):
# *_PRIVATE_NETWORKS allow loopback / RFC1918 / ULA
# *_INSECURE_HTTP allow http:// instead of https://
# WEBAUTHN_*_ORIGINS passkeys over http:// (LAN dev)
# LDAP_*_PORT_389 plain LDAP on :389 (trusted LAN)
# APP_PDNS_ALLOW_PRIVATE_NETWORKS: "true"
# APP_PDNS_ALLOW_INSECURE_HTTP: "true"
# APP_OIDC_ALLOW_PRIVATE_NETWORKS: "true"
# APP_OIDC_ALLOW_INSECURE_HTTP: "true"
# WEBAUTHN_ALLOW_INSECURE_ORIGINS: "true"
# LDAP_ALLOW_INSECURE_PORT_389: "true"
volumes:
- app-data:/data
- ./provisioning.yaml:/etc/pda/provisioning.yaml:ro
volumes:
app-data:
# provisioning.yaml - applied once on first boot, then the UI
# is the source of truth. Every block is optional.
settings:
site_name: ACME DNS
# Register your PowerDNS Authoritative backend(s)
pdns_servers:
- slug: primary
name: Primary PowerDNS
base_url: https://pdns.internal:8081/api/v1
server_id: localhost
api_key: change-me-pdns-api-key # encrypted on boot
is_default: true
.env next to the file:openssl rand -base64 32 → APP_SECRET_KEY & APP_ENCRYPTION_KEY.
APP_URL to the real URL the browser uses, and your BOOTSTRAP_ADMIN_* values.docker compose up -d → first login with the bootstrap admin.provisioning.yaml (second tab) to register backends, roles, and teams on first boot - zero clicks.Flip PDNS_BACKGROUND_POLLING to "true" for primary/secondary or cluster topologies - it enables the live sync state, drift advisories, and the dashboard PowerDNS-metrics tab. Off by default for standalone installs.
Postgres + Redis is the path for HA (replicas > 1) - see the installation guide. Just evaluating? Spin up the live demo instead.
PowerDNS-AuthAdmin is MIT-licensed and developed in public. Star the repo, open an issue, file a feature request, or send a pull request - contributions of every size are welcome. There's no SaaS lock-in and no telemetry phone-home.