{"id":51055865,"url":"https://github.com/cyr-ius/powerdns-ui","last_synced_at":"2026-06-22T21:01:33.653Z","repository":{"id":353819802,"uuid":"1220230546","full_name":"cyr-ius/powerdns-ui","owner":"cyr-ius","description":"Web management interface for PowerDNS Authoritative Server","archived":false,"fork":false,"pushed_at":"2026-06-19T01:17:01.000Z","size":10554,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-19T03:10:16.147Z","etag":null,"topics":["admin","dns","gui","powerdns","powerdns-admin","powerdns-ui","powerdnsui"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cyr-ius.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"cyr-ius"}},"created_at":"2026-04-24T17:26:13.000Z","updated_at":"2026-06-19T01:16:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cyr-ius/powerdns-ui","commit_stats":null,"previous_names":["cyr-ius/powerdns-ui"],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/cyr-ius/powerdns-ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyr-ius%2Fpowerdns-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyr-ius%2Fpowerdns-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyr-ius%2Fpowerdns-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyr-ius%2Fpowerdns-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cyr-ius","download_url":"https://codeload.github.com/cyr-ius/powerdns-ui/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyr-ius%2Fpowerdns-ui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34665261,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin","dns","gui","powerdns","powerdns-admin","powerdns-ui","powerdnsui"],"created_at":"2026-06-22T21:01:32.836Z","updated_at":"2026-06-22T21:01:33.647Z","avatar_url":"https://github.com/cyr-ius.png","language":"HTML","funding_links":["https://github.com/sponsors/cyr-ius"],"categories":[],"sub_categories":[],"readme":"# PowerDNS UI\n\nWeb management interface for [PowerDNS Authoritative Server](https://www.powerdns.com/auth.html).\n\n![License](https://img.shields.io/badge/license-MIT-blue)\n![Python](https://img.shields.io/badge/python-3.12%2B-blue)\n![Angular](https://img.shields.io/badge/angular-21-red)\n\n---\n\n## Table of Contents\n\n- [Features](#features)\n- [Architecture](#architecture)\n- [Quick Start](#quick-start)\n  - [Docker Compose](#docker-compose)\n  - [Build from Source](#build-from-source)\n- [PowerDNS Configuration](#powerdns-configuration)\n- [Environment Variables](#environment-variables)\n- [MariaDB Backend (gmysql)](#mariadb-backend-gmysql)\n  - [When to run it](#when-to-run-it)\n  - [Usage](#usage)\n  - [Custom SQL schema](#--schema----custom-sql-file)\n  - [Recommended startup order](#recommended-startup-order)\n- [Catalog Zones](#catalog-zones)\n  - [Producer](#producer)\n  - [Consumer](#consumer)\n- [Lua Records](#lua-records)\n- [OIDC Authentication](#oidc-authentication)\n- [Roles and Permissions](#roles-and-permissions)\n- [Audit Log](#audit-log)\n- [API](#api)\n- [Development](#development)\n  - [Prerequisites](#prerequisites)\n  - [Backend](#backend)\n  - [Frontend](#frontend)\n- [Screenshots](#screenshots)\n- [Demos](#demos)\n- [License](#license)\n\n---\n\n## Features\n\n- **DNS Zones** — create, edit, delete (Native / Master / Slave), record management with catalog assignment at creation\n- **DNSSEC** — cryptographic key management per zone\n- **Reverse DNS** — IPv4/IPv6 PTR zone creation with automatic PTR record generation\n- **Lua Records** — per-zone activation of dynamic Lua records (admin/zone-admin only), automatically adds the `LUA` record type\n- **Catalog Zones** — Producer zones (manual member management) and Consumer zones (automatic sync via AXFR from a Producer)\n- **ACME Keys** — per-zone and per-user API keys for DNS-01 ACME challenges (Let's Encrypt / cert-manager integration)\n- **TSIG Keys** — creation and management of signing keys\n- **Autoprimaries** — automatic primary server configuration\n- **DNS Views** _(LMDB only)_ — split-horizon, zone ↔ view association\n- **Networks** _(LMDB only)_ — network assignment to views\n- **Search** — global search across zones, records, and comments\n- **Statistics** — real-time PowerDNS server metrics\n- **Server Configuration** — active configuration visualization\n- **Audit Log** — history of all user actions + PDNS logs, export to syslog\n- **User Management** — admin / manager / viewer roles per account\n- **OIDC SSO** — delegated authentication (Keycloak, Authentik, etc.)\n- **Theme** — light / dark / automatic\n\n## Architecture\n\n```\n┌─────────────────────────────────────┐\n│          Browser (Angular 22)       │\n└───────────────┬─────────────────────┘\n                │ HTTP\n┌───────────────▼─────────────────────┐\n│        FastAPI  (Python 3.12+)      │\n│        SQLite  (SQLModel)           │\n└───────────────┬─────────────────────┘\n                │ REST API\n┌───────────────▼─────────────────────┐\n│   PowerDNS Authoritative Server     │\n└─────────────────────────────────────┘\n```\n\nThe Angular frontend is served statically by FastAPI — a single container is sufficient.\n\n## Quick Start\n\n### Docker Compose\n\n```yaml\n# docker-compose.yaml\nservices:\n  pdns:\n    image: powerdns/pdns-auth-50\n    restart: unless-stopped\n    volumes:\n      - powerdns_data:/var/lib/powerdns\n    ports:\n      - 53:53\n      - 53:53/udp\n    command:\n      - \"--api=yes\"\n      - \"--api-key=${PDNS_AUTH_API_KEY:-change-this-api-key-in-production}\"\n      - \"--webserver=yes\"\n      - \"--webserver-address=0.0.0.0\"\n      - \"--webserver-port=8081\"\n      - \"--webserver-allow-from=0.0.0.0/0\"\n      - \"--loglevel=6\"\n      - \"--loglevel-show=yes\"\n\n  pdns-ui:\n    image: ghcr.io/cyr-ius/pdns-ui:latest\n    restart: unless-stopped\n    depends_on: [pdns]\n    environment:\n      - ADMIN_PASSWORD=changeme\n      - SECRET_KEY=change-this-secret-key-in-production\n      - PDNS_AUTH_API_URL=http://pdns:8081\n      - PDNS_AUTH_API_KEY=change-this-api-key-in-production\n    volumes:\n      - powerdns-ui_data:/var/lib/powerdns-ui\n    ports:\n      - 8080:8080/tcp\n    depends_on:\n      - pdns\n\nvolumes:\n  powerdns_data:\n  powerdns-ui_data:\n\n```\n\n```bash\ndocker compose up -d\n```\n\nAccess: http://localhost:8080 — default credentials: `admin` / `changeme`\n\n### Build from Source\n\n```bash\n# Build with version\ndocker build --build-arg VERSION=1.2.0 -t powerdns-ui .\n\n# Without version (default: 1.0.0)\ndocker build -t powerdns-ui .\n```\n\n## PowerDNS Configuration\n\nEnable the REST API in `pdns.conf`:\n\n```ini\nwebserver=yes\nwebserver-address=0.0.0.0\nwebserver-port=8081\nwebserver-allow-from=127.0.0.1,172.16.0.0/12,192.168.0.0/16\n```\n\nFor **DNS Views** and **Networks** (LMDB backend only):\n\n```ini\nlaunch=lmdb\nlmdb-filename=/var/lib/powerdns/pdns.lmdb\nviews=yes\n```\n\n\u003e The _Views_ and _Networks_ menus only appear in the interface if the detected backend is `lmdb`.\n\n## Environment Variables\n\n| Variable                      | Default                             | Description                                                    |\n| ----------------------------- | ----------------------------------- | -------------------------------------------------------------- |\n| `ADMIN_USERNAME`              | `admin`                             | Super-administrator account name created at startup            |\n| `ADMIN_PASSWORD`              | `changeme`                          | Initial admin password                                         |\n| `SECRET_KEY`                  | _(change this)_                     | JWT signing key — **must be changed in production**            |\n| `ACCESS_TOKEN_EXPIRE_MINUTES` | `480`                               | Token validity duration (minutes)                              |\n| `PDNS_AUTH_API_URL`           | `http://pdns:8081`                  | PowerDNS REST API URL                                          |\n| `PDNS_AUTH_API_KEY`           | `change-this-api-key-in-production` | PowerDNS API key (`api-key` in pdns.conf)                      |\n| `DATABASE_URL`                | `sqlite+aiosqlite:///…/database.db` | Database URL                                                   |\n| `DATA_DIR`                    | `/var/lib/powerdns-ui`              | Data directory (SQLite)                                        |\n| `LOG_LEVEL`                   | `INFO`                              | Log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`)                |\n| `APP_VERSION`                 | `1.0.0`                             | Application version (injected via `--build-arg VERSION=x.y.z`) |\n\n## MariaDB Backend (gmysql)\n\nWhen PowerDNS is configured with the `gmysql` backend, the database schema must be present before the server starts. The UI container bundles the official PDNS schema and exposes a one-shot script to apply it.\n\n### When to run it\n\n- **First deployment** — before starting `pdns` for the first time.\n- **After recreating the database volume** — the schema is lost along with the data.\n\n\u003e The script is idempotent: if the `domains` table already exists it exits immediately without touching anything.\n\n### Usage\n\n```\npython init_pdns_schema.py --host HOST [options]\n\nrequired:\n  --host HOST          MariaDB host\n\noptional:\n  --port PORT          MariaDB port           (default: 3306)\n  --user USER          MariaDB user           (default: powerdns)\n  --password PASSWORD  MariaDB password       (default: pdns)\n  --database DATABASE  MariaDB database       (default: powerdns)\n  --schema FILE_OR_URL SQL schema to apply (see below)\n```\n\n**Via `docker compose run`** (one-shot, before or after `up`):\n\n```bash\ndocker compose run --rm pdns-ui \\\n  python /app/backend/scripts/init_pdns_schema.py \\\n  --host pdns-db --password pdns\n```\n\n**Local development:**\n\n```bash\npython backend/scripts/init_pdns_schema.py \\\n  --host localhost --user powerdns --password pdns --database powerdns\n```\n\n### `--schema` — custom SQL file\n\nWhen `--schema` is omitted the script uses the file bundled in the image (`/var/lib/powerdns/schema.sql`), then falls back to `docker/pdns_schema.sql` in the repository.\n\nYou can override it with:\n\n| Value passed                            | Behaviour                                                                                                     |\n| --------------------------------------- | ------------------------------------------------------------------------------------------------------------- |\n| Local path (`/tmp/my.sql`)              | Reads the file directly                                                                                       |\n| Full URL (`https://…/schema.mysql.sql`) | Downloads and applies                                                                                         |\n| Filename only (`schema.mysql.sql`)      | Downloads from the [PowerDNS GitHub repo](https://github.com/PowerDNS/pdns/tree/master/modules/gmysqlbackend) |\n\n```bash\n# Use a specific file from the PowerDNS GitHub repo (branch: master)\ndocker exec pdns-ui \\\n  python /app/backend/scripts/init_pdns_schema.py \\\n  --host pdns-db --password pdns \\\n  --schema schema.mysql.sql\n```\n\n### Example output\n\n```\nINFO: Connecting to MariaDB powerdns@pdns-db:3306/powerdns ...\nINFO: Using bundled schema: /var/lib/powerdns/schema.sql\nINFO: PDNS schema successfully created in 'powerdns'.\n```\n\n### Recommended startup order\n\n```\npdns-db  →  init_pdns_schema  →  pdns  →  pdns-ui\n```\n\n---\n\n## Catalog Zones\n\nCatalog zones (RFC 9432) allow a PowerDNS server to automatically distribute zone configurations to secondary servers.\n\n### Producer\n\nA **Producer** catalog zone is a primary zone that lists member zones. When a zone is added to a Producer, secondary Consumer servers discover and provision it automatically.\n\n- Create a Producer from the **Catalogs → Producer** tab\n- Assign an existing zone to a Producer at creation time or from the Catalogs page\n- Add / remove member zones manually from the Producer's member list\n\n### Consumer\n\nA **Consumer** catalog zone is a secondary zone that pulls its configuration from a Producer via AXFR. Member zones are created automatically by PowerDNS after each zone transfer — they cannot be managed manually.\n\n- Create a Consumer from the **Catalogs → Consumer** tab\n- Provide the name of the catalog (must match the Producer's zone name) and the IP address(es) of the Producer server\n- The received member zones are displayed in read-only mode\n\n---\n\n## Lua Records\n\n[Lua Records](https://doc.powerdns.com/authoritative/lua-records/index.html) allow dynamic DNS responses generated by Lua scripts embedded directly in zone records.\n\nActivation is per-zone and restricted to **Super Admins** and **Zone Admins**:\n\n1. Open a zone → **Settings** tab\n2. Enable the **Lua Records** toggle\n3. The `LUA` record type is automatically added to the zone's available types\n\n\u003e Lua Records must also be enabled at the PowerDNS server level (`enable-lua-records=yes` in `pdns.conf`).\n\n---\n\n## OIDC Authentication\n\nSSO configuration (Keycloak, Authentik, …) is done entirely from the web interface: **Administration → OIDC**. No environment variables are required — settings are stored in the database.\n\nConfigurable fields: enable/disable, Client ID, Client Secret, Discovery URL, Redirect URI, scopes, disable local login.\n\n## Roles and Permissions\n\n| Role              | Zones       | Records      | Members            | Zone Settings (Lua Records, Record Types) |\n| ----------------- | ----------- | ------------ | ------------------ | ----------------------------------------- |\n| **Super Admin**   | All         | Read / Write | Full management    | ✅ All zones                              |\n| **Account Admin** | Own account | Read / Write | Account management | —                                         |\n| **Manager**       | Own account | Read / Write | —                  | —                                         |\n| **Viewer**        | Own account | Read only    | —                  | —                                         |\n| **Zone Admin**    | Own zone    | Read / Write | Zone management    | ✅ Own zone                               |\n\nUsers are grouped by **accounts**. Each account is associated with zones and users with their role.\n\n\u003e **Zone Admin** is a per-zone role assignable from the zone's _Members_ tab. It grants full control over that zone's settings, including enabling Lua Records and customizing available record types.\n\n## Audit Log\n\nAll actions are tracked: logins, zone/record modifications, user management, configuration changes.\n\n**Syslog Export**: configurable from the _Audit Log_ page (_Syslog: active/inactive_ button) — host, port, UDP/TCP protocol, facility.\n\n## API\n\nInteractive documentation is available on the running instance:\n\n- Swagger UI: http://localhost:8080/api/docs\n\n## Development\n\n### Prerequisites\n\n- Python 3.12+, [uv](https://docs.astral.sh/uv/)\n- Node.js 22+\n\n### Backend\n\n```bash\ncd backend\nuv sync\nuv run uvicorn app.main:app --reload --port 8080\n```\n\n### Frontend\n\n```bash\ncd frontend\nnpm install\nnpm run start   # proxy to localhost:8080\n```\n\n## Screenshots\n\n\u003cimg width=\"1151\" height=\"680\" alt=\"image\" src=\"https://github.com/user-attachments/assets/542b271b-f806-4be9-9ebf-58d04e4d0676\" /\u003e\n\u003cimg width=\"1151\" height=\"680\" alt=\"image\" src=\"https://github.com/user-attachments/assets/4a17d385-c4cc-4772-923f-4a7a696cbc81\" /\u003e\n\u003cimg width=\"1151\" height=\"680\" alt=\"image\" src=\"https://github.com/user-attachments/assets/e3718deb-2b8b-47ea-9515-dcaeab94e6ed\" /\u003e\n\u003cimg width=\"1151\" height=\"680\" alt=\"image\" src=\"https://github.com/user-attachments/assets/36df2912-52e6-4890-8d6b-e2322583b774\" /\u003e\n\n## Demos\n\n\u003cimg width=\"640\" height=\"480\" alt=\"Capture vidéo du 2026-04-27 12-31-15(1)\" src=\"https://github.com/user-attachments/assets/4a5eb088-0441-42ab-a926-8d22ec835112\" /\u003e\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyr-ius%2Fpowerdns-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyr-ius%2Fpowerdns-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyr-ius%2Fpowerdns-ui/lists"}