Skip to content

Docker Installation

Poweradmin provides official Docker images for easy deployment with FrankenPHP.

Docker Images

Official images are available at:

Image Tags

The image tags published for production and development use are:

Tag Source branch Description
stable release/4.3.x Tracks the latest tagged release on release/4.3.x.
4.4.x release/4.4.x LTS line until December 2027; updates on every commit to the branch.
4.3.x release/4.3.x Maintenance line, end of life three months after the 4.5.0 release; updates on every commit to the branch.
4.2.x release/4.2.x Maintenance line, end of life three months after the 4.5.0 release; updates on every commit to the branch.
latest master Tracks master, which carries the newest release line between patch releases.
dev develop Development tip - not for production.
lts release/3.x Long-term support for the 3.x series.
4.4.1, 4.3.5 Tagged release Pin to a specific version, for example 4.4.1 on the LTS line - recommended for production. Image tags carry no v prefix: the v4.4.1 git tag is published as 4.4.1 (plus 4.4 and 4).

Note: The next tag was removed when the release branch structure changed. The per-version tags (4.4.1) are the safest choices for production; the branch tags (4.2.x, 4.3.x, 4.4.x) update on every push and may include unreleased fixes. For moving an existing container to a newer release, see Upgrading a Docker deployment.

Quick Start

SQLite

docker run -d --name poweradmin -p 80:80 \
  -e DB_TYPE=sqlite \
  -e PA_CREATE_ADMIN=1 \
  -v poweradmin-db:/db \
  poweradmin/poweradmin:stable

Warning: DB_TYPE is required. There is no default. A container started without it logs ERROR: DB_TYPE environment variable is required and exits immediately.

Check logs for the generated admin password:

docker logs poweradmin | grep -i password

MySQL

docker run -d --name poweradmin -p 80:80 \
  -e PA_CREATE_ADMIN=1 \
  -e DB_TYPE=mysql \
  -e DB_HOST=mysql-server \
  -e DB_USER=poweradmin \
  -e DB_PASS=your-password \
  -e DB_NAME=poweradmin \
  -e DNS_NS1=ns1.example.com \
  -e DNS_NS2=ns2.example.com \
  -e DNS_HOSTMASTER=hostmaster.example.com \
  poweradmin/poweradmin:stable

PostgreSQL

docker run -d --name poweradmin -p 80:80 \
  -e PA_CREATE_ADMIN=1 \
  -e DB_TYPE=pgsql \
  -e DB_HOST=postgres-server \
  -e DB_USER=poweradmin \
  -e DB_PASS=your-password \
  -e DB_NAME=poweradmin \
  -e DNS_NS1=ns1.example.com \
  -e DNS_NS2=ns2.example.com \
  -e DNS_HOSTMASTER=hostmaster.example.com \
  poweradmin/poweradmin:stable

Note: The DB_NAME database and the DB_USER (with privileges on it) must already exist - the container does not create them. With the official mysql/postgres images, set MYSQL_DATABASE/MYSQL_USER/MYSQL_PASSWORD (or POSTGRES_DB/POSTGRES_USER/POSTGRES_PASSWORD) so they are provisioned on first start. Setting only the root password leaves the server with no Poweradmin database or user and fails with ERROR 1045 Access denied.

On startup the container loads the Poweradmin schema into an empty DB_NAME database automatically; an already-populated database is left untouched. PowerDNS stores its own zones and records in a separate database that PowerDNS (not Poweradmin) creates and initializes. If you instead keep both in one shared database, set PA_INIT_PDNS_SCHEMA=true to have the container load the PowerDNS schema into an empty DB_NAME as well; it is off by default and skipped when PA_PDNS_DB_NAME is set.

Docker Compose

Basic Setup with MySQL

version: '3.8'

services:
  poweradmin:
    image: poweradmin/poweradmin:stable
    ports:
      - "80:80"
    environment:
      PA_CREATE_ADMIN: "true"
      PA_ADMIN_PASSWORD: "change-me"
      DB_TYPE: mysql
      DB_HOST: mysql
      DB_USER: poweradmin
      DB_PASS: poweradmin-password
      DB_NAME: poweradmin
      DNS_NS1: ns1.example.com
      DNS_NS2: ns2.example.com
      DNS_HOSTMASTER: hostmaster.example.com
    depends_on:
      - mysql

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root-password
      MYSQL_DATABASE: poweradmin
      MYSQL_USER: poweradmin
      MYSQL_PASSWORD: poweradmin-password
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data:

With PowerDNS

version: '3.8'

services:
  poweradmin:
    image: poweradmin/poweradmin:stable
    ports:
      - "8080:80"
    environment:
      PA_CREATE_ADMIN: "true"
      DB_TYPE: mysql
      DB_HOST: mysql
      DB_USER: poweradmin
      DB_PASS: poweradmin-password
      DB_NAME: poweradmin
      PA_PDNS_DB_NAME: pdns
      DNS_NS1: ns1.example.com
      DNS_NS2: ns2.example.com
      DNS_HOSTMASTER: hostmaster.example.com
      PA_PDNS_API_URL: http://powerdns:8081
      PA_PDNS_API_KEY: your-api-key
    depends_on:
      - mysql
      - powerdns

  powerdns:
    image: powerdns/pdns-auth-49
    ports:
      - "53:53/udp"
      - "53:53/tcp"
    volumes:
      - ./pdns.conf:/etc/powerdns/pdns.conf:ro
    depends_on:
      - mysql

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root-password
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

volumes:
  mysql-data:

The official powerdns/pdns-auth-* images do not read PDNS_* environment variables — that convention belongs to third-party images, and the official image silently ignores them (see PowerDNS/pdns#14951). The only configuration variable it honors is PDNS_AUTH_API_KEY; everything else has to come from a config file or command-line arguments. The example above mounts a pdns.conf next to the compose file:

launch=gmysql
gmysql-host=mysql
gmysql-user=pdns
gmysql-password=pdns-password
gmysql-dbname=pdns

api=yes
api-key=your-api-key
webserver=yes
webserver-address=0.0.0.0
webserver-allow-from=0.0.0.0/0

The example also relies on ./init.sql to prepare MySQL on first startup: it must create both databases and users, and load the PowerDNS schema into the pdns database. Start with:

CREATE DATABASE poweradmin;
CREATE USER 'poweradmin'@'%' IDENTIFIED BY 'poweradmin-password';
GRANT ALL PRIVILEGES ON poweradmin.* TO 'poweradmin'@'%';

CREATE DATABASE pdns;
CREATE USER 'pdns'@'%' IDENTIFIED BY 'pdns-password';
GRANT ALL PRIVILEGES ON pdns.* TO 'pdns'@'%';

-- Poweradmin manages zones in the PowerDNS database (PA_PDNS_DB_NAME)
GRANT ALL PRIVILEGES ON pdns.* TO 'poweradmin'@'%';

USE pdns;

then append the official PowerDNS MySQL schema matching your PowerDNS version:

curl https://raw.githubusercontent.com/PowerDNS/pdns/rel/auth-5.1.x/modules/gmysqlbackend/schema.mysql.sql >> init.sql

Admin User Creation

The container can automatically create an admin user on first startup:

Variable Default Description
PA_CREATE_ADMIN false Enable admin creation (true/1/yes)
PA_ADMIN_USERNAME admin Admin username
PA_ADMIN_PASSWORD (auto) Admin password (auto-generated if not set)
PA_ADMIN_EMAIL admin@example.com Admin email
PA_ADMIN_FULLNAME Administrator Admin display name

If PA_ADMIN_PASSWORD is not set, a secure password is generated and logged:

docker logs poweradmin | grep -i password

Note: The admin user is only created if it doesn't already exist.

Key Environment Variables

This page covers the variables you need to stand a deployment up and secure it. It is deliberately a subset: the container accepts roughly 330 variables, and the complete reference, versioned alongside the code, is DOCKER.md in the source repository. Check there for anything not listed below, especially the interface, mail, DNS validation and per-provider OIDC and SAML settings.

Every variable here can also be supplied from a file by appending __FILE to its name; see Docker Secrets.

Note: These variables only take effect when the container generates its own configuration. If config/settings.php (or PA_CONFIG_PATH) exists and is not empty, that file is used and the variables below are ignored. See Configuration Priority.

Database

Variable Default Description
DB_TYPE (required) Database type: sqlite, mysql, pgsql. The container exits at startup if this is unset
DB_HOST - Database hostname
DB_PORT - Database port (3306 for MySQL, 5432 for PostgreSQL)
DB_USER - Database username
DB_PASS - Database password
DB_NAME - Database name
PA_PDNS_DB_NAME - Separate PowerDNS database (MySQL only)
PA_INIT_PDNS_SCHEMA false Load PowerDNS schema into an empty DB_NAME on startup (MySQL/PostgreSQL; skipped when PA_PDNS_DB_NAME is set)
DB_WAIT_TIMEOUT 30 Seconds to wait for the MySQL/PostgreSQL server before schema initialization is skipped

DNS

Variable Default Description
DNS_NS1 ns1.example.com Primary nameserver
DNS_NS2 ns2.example.com Secondary nameserver
DNS_HOSTMASTER hostmaster.example.com Hostmaster email
PA_DNS_BACKEND sql DNS data backend: sql (direct database) or api (PowerDNS REST API, v4.3.0+). See PowerDNS API

Security

Session and password hashing

Variable Default Description
PA_SESSION_KEY (auto) Session encryption key. Set this explicitly in production so sessions survive a container restart
PA_PASSWORD_ENCRYPTION bcrypt Password hashing: bcrypt, argon2i, argon2id. The legacy md5 and md5salt options were removed in 4.3.0
PA_PASSWORD_COST 12 Cost factor for bcrypt hashing
PA_LOGIN_TOKEN_VALIDATION true Enable CSRF token validation for login
PA_GLOBAL_TOKEN_VALIDATION true Enable CSRF token validation for all forms

Password policy

Variable Default Description
PA_PASSWORD_RULES_ENABLED true Enable password policy enforcement
PA_PASSWORD_MIN_LENGTH 6 Minimum password length
PA_PASSWORD_REQUIRE_UPPERCASE true Require at least one uppercase letter
PA_PASSWORD_REQUIRE_LOWERCASE true Require at least one lowercase letter
PA_PASSWORD_REQUIRE_NUMBERS true Require at least one number
PA_PASSWORD_REQUIRE_SPECIAL false Require at least one special character
PA_PASSWORD_SPECIAL_CHARACTERS !@#$%^&*()+-=[]{}\|;:,.<>? Characters that count as special for the rule above (v4.6.0+)

See Password Policies for the full policy. Before 4.6.0 the special-character set has no environment variable and must be set in settings.php.

Account lockout

Variable Default Description
PA_LOCKOUT_ENABLED false Enable account lockout after failed logins
PA_LOCKOUT_ATTEMPTS 5 Failed attempts before lockout
PA_LOCKOUT_DURATION 15 Lockout duration in minutes
PA_LOCKOUT_TRACK_IP true Lock accounts based on IP address
PA_LOCKOUT_CLEAR_ON_SUCCESS true Clear failed attempts after a successful login
PA_LOCKOUT_WHITELIST_IPS - Comma-separated IPs, CIDRs or wildcards that are never locked out; wins over the blacklist (v4.6.0+)
PA_LOCKOUT_BLACKLIST_IPS - Comma-separated IPs, CIDRs or wildcards that are always blocked (v4.6.0+)

Before 4.6.0 the IP whitelist and blacklist have no environment variables and must be set in settings.php. See Security Policies.

Multi-factor authentication

Variable Default Description
PA_MFA_ENABLED false Enable multi-factor authentication
PA_MFA_ENFORCED false Enforce MFA for users holding user_enforce_mfa
PA_MFA_APP_ENABLED true Offer the authenticator app method. Ignored while email verification is unusable, so the last remaining method stays available
PA_MFA_EMAIL_ENABLED true Offer the email verification method
PA_MFA_RECOVERY_CODES 8 Number of recovery codes generated
PA_MFA_RECOVERY_CODE_LENGTH 10 Length of each recovery code
PA_MFA_SKIP_FOR_EXTERNAL_AUTH false Skip enforcement for LDAP, OIDC and SAML logins, trusting the identity provider (v4.5.0+)
PA_MFA_MAX_VERIFY_ATTEMPTS 5 Failed second-factor guesses before the code is refused (v4.5.0+)
PA_MFA_VERIFY_LOCKOUT_DURATION 15 Minutes to refuse further attempts once the limit is hit (v4.5.0+)

Email-based MFA needs a working mail configuration; see Mail.

Password reset

Variable Default Description
PA_PASSWORD_RESET_ENABLED false Enable password reset functionality
PA_PASSWORD_RESET_TOKEN_LIFETIME 3600 Token validity in seconds
PA_PASSWORD_RESET_RATE_LIMIT_ATTEMPTS 5 Maximum reset attempts per time window
PA_PASSWORD_RESET_RATE_LIMIT_WINDOW 3600 Rate limit window in seconds
PA_PASSWORD_RESET_MIN_TIME_BETWEEN 60 Minimum seconds between requests

Username recovery

Variable Default Description
PA_USERNAME_RECOVERY_ENABLED false Enable username recovery functionality
PA_USERNAME_RECOVERY_RATE_LIMIT_ATTEMPTS 5 Maximum recovery attempts per time window
PA_USERNAME_RECOVERY_RATE_LIMIT_WINDOW 3600 Rate limit window in seconds
PA_USERNAME_RECOVERY_MIN_TIME_BETWEEN 60 Minimum seconds between requests

reCAPTCHA

Variable Default Description
PA_RECAPTCHA_ENABLED false Enable reCAPTCHA on login
PA_RECAPTCHA_SITE_KEY (empty) Site key (public)
PA_RECAPTCHA_SECRET_KEY (empty) Secret key (private)
PA_RECAPTCHA_VERSION v3 reCAPTCHA version: v2 or v3
PA_RECAPTCHA_V3_THRESHOLD 0.5 Score threshold for v3, from 0.0 to 1.0

Proxies

Variable Default Description
TRUSTED_PROXIES - Comma-separated proxy CIDRs, or private_ranges, allowed to set forwarded-IP headers. See Reverse Proxy

Account lockout (with PA_LOCKOUT_TRACK_IP) and the reset and recovery rate limits all throttle by client IP as well as by account. Behind a proxy they need TRUSTED_PROXIES set, or every request appears to come from the proxy's address.

PowerDNS API

Variable Default Description
PA_PDNS_API_URL - PowerDNS API URL
PA_PDNS_API_KEY - PowerDNS API key

Modules

Variable Default Description
PA_MODULE_CSV_EXPORT_ENABLED true Enable CSV export module
PA_MODULE_ZONE_IMPORT_EXPORT_ENABLED false Enable zone import/export module
PA_MODULE_ZONE_IMPORT_EXPORT_AUTO_TTL 300 Default TTL for imported records (seconds)
PA_MODULE_ZONE_IMPORT_EXPORT_MAX_FILE_SIZE 1048576 Max upload file size in bytes
PA_MODULE_WHOIS_ENABLED false Enable WHOIS lookup module
PA_MODULE_WHOIS_RESTRICT_TO_ADMIN true Restrict WHOIS to administrators
PA_MODULE_WHOIS_DEFAULT_SERVER (empty) Default WHOIS server
PA_MODULE_WHOIS_CUSTOM_SERVERS (empty) Custom TLD-to-server mapping, e.g. za=whois.registry.net.za
PA_MODULE_WHOIS_SOCKET_TIMEOUT 10 WHOIS socket timeout in seconds
PA_MODULE_RDAP_ENABLED false Enable RDAP lookup module
PA_MODULE_RDAP_RESTRICT_TO_ADMIN true Restrict RDAP to administrators
PA_MODULE_RDAP_DEFAULT_SERVER (empty) Default RDAP server
PA_MODULE_RDAP_CUSTOM_SERVERS (empty) Custom TLD-to-server mapping, e.g. za=https://rdap.example.com/
PA_MODULE_RDAP_REQUEST_TIMEOUT 10 RDAP request timeout in seconds
PA_MODULE_EMAIL_PREVIEWS_ENABLED false Enable the email template previews module
PA_MODULE_EMAIL_PREVIEWS_RESTRICT_TO_ADMIN true Restrict email previews to administrators
PA_MODULE_DNS_WIZARDS_ENABLED false Enable the DNS record wizards module
PA_MODULE_DNS_WIZARDS_TYPES DMARC,SPF,DKIM,CAA,TLSA,SRV Comma-separated list of DNS wizard types

This table covers every module variable the container accepts. Two module settings have no environment variable and must be set in settings.php: modules.secondary_zone_import.enabled and the modules.dns_wizards.caa_providers list.

For detailed module configuration, see the Configuration section.

Authentication

Variable Default Description
PA_LDAP_ENABLED false Enable LDAP authentication
PA_LDAP_URI (empty) LDAP server URI, e.g. ldaps://ldap.example.com:636
PA_LDAP_BASE_DN (empty) Base DN where users are stored
PA_LDAP_BIND_DN (empty) Bind DN used to search the directory
PA_LDAP_BIND_PASSWORD (empty) Password for the bind DN
PA_LDAP_SEARCH_FILTER (empty) Additional LDAP search filter
PA_LDAP_PROTOCOL_VERSION 3 LDAP protocol version
PA_LDAP_USER_ATTRIBUTE uid User attribute (uid for OpenLDAP, sAMAccountName for AD)
PA_LDAP_SYNC_USER_INFO false Sync fullname/email from LDAP on login (v4.5.0+)
PA_LDAP_AUTO_PROVISION false Create missing users on first LDAP login (v4.5.0+)
PA_LDAP_PERMISSION_TEMPLATE_MAPPING - LDAP group to permission template mapping, group:Template comma-separated (v4.5.0+)
PA_LDAP_GROUP_MAPPING - LDAP group to Poweradmin group mapping, group:PAGroup comma-separated (v4.5.0+)
PA_OIDC_ENABLED false Enable OpenID Connect
PA_SAML_ENABLED false Enable SAML authentication
PA_APPLICATION_URL (empty) Public base URL. Required when OIDC or SAML is enabled, and for password reset and emailed links
PA_BASE_URL (empty) Legacy base URL. Accepted as a URL source for SAML only

From 4.2.6, 4.3.5, 4.4.1 and 4.5.0 the container refuses to start when OIDC or SAML is enabled without a URL source, because the redirect and SP URLs can no longer be derived from the request. OIDC accepts only PA_APPLICATION_URL; SAML also accepts PA_BASE_URL, or all three of PA_SAML_SP_ENTITY_ID, PA_SAML_SP_ACS_URL and PA_SAML_SP_SLS_URL. The check runs only when the entrypoint generates the configuration - if you mount your own settings.php (see above), these variables are ignored and the check is skipped.

The LDAP rows above are enough for a working LDAP setup. OIDC and SAML are not - each provider needs its own block of variables (PA_OIDC_AZURE_*, PA_SAML_OKTA_* and so on), and there are 137 authentication variables in total across LDAP, OIDC and SAML (web server authentication is listed below). They are listed in DOCKER.md; the settings behind them are explained in OIDC and SAML.

Web server authentication (REMOTE_USER)

Signs users in as the user name an authenticating web server or reverse proxy supplies. Added in 4.6.0. See Web Server Authentication for how it works and the security requirements.

Variable Default Description
PA_REMOTE_USER_ENABLED false Enable web server authentication
PA_REMOTE_USER_SERVER_VARIABLE REMOTE_USER Server variable that holds the user name
PA_REMOTE_USER_HEADER (empty) Read the user name from this request header instead (header mode), e.g. Remote-User
PA_REMOTE_USER_TRUSTED_PROXIES (empty) Comma-separated proxy IPs or CIDRs allowed to send the header. Empty means the header is ignored. Required in header mode
PA_REMOTE_USER_STRIP_REALM false Turn user@REALM and DOMAIN\user into user
PA_REMOTE_USER_EMAIL_ATTRIBUTE (empty) Variable or header holding the email address, e.g. Remote-Email
PA_REMOTE_USER_NAME_ATTRIBUTE (empty) Variable or header holding the full name, e.g. Remote-Name
PA_REMOTE_USER_GROUPS_ATTRIBUTE (empty) Variable or header holding the groups, e.g. Remote-Groups
PA_REMOTE_USER_GROUPS_SEPARATOR , Separator between groups in the groups attribute
PA_REMOTE_USER_LOGOUT_URL (empty) Where to send users after logout, to end the proxy's own session
PA_REMOTE_USER_HIDE_LOGIN_FORM false Hide the password form while the web server signs users in
PA_REMOTE_USER_AUTO_PROVISION true Create an account on first sign-in
PA_REMOTE_USER_ALLOW_SUPERUSER_PROVISIONING false Let group mappings grant the superuser flag
PA_REMOTE_USER_SYNC_USER_INFO true Update name and email from the attributes on each sign-in
PA_REMOTE_USER_DEFAULT_PERMISSION_TEMPLATE Guest Permission template for new accounts when no mapping matches
PA_REMOTE_USER_PERMISSION_TEMPLATE_MAPPING (empty) Map groups to permission templates (format: group1=Template1,group2=Template2)
PA_REMOTE_USER_GROUP_MAPPING (empty) Map groups to Poweradmin groups (format: group1=PaGroup1,group2=PaGroup2)

Notes:

  • The image's Caddy server does not authenticate users itself. In Docker this is normally used in header mode, behind an authenticating proxy (Authelia, oauth2-proxy, Authentik outpost) that sets Remote-User.
  • PA_REMOTE_USER_TRUSTED_PROXIES must list the proxy's address as Poweradmin sees it. It is not taken from TRUSTED_PROXIES. With a header set but no trusted proxies the header is ignored, and the container logs a warning at startup.
  • The container must not be reachable except through that proxy, and the proxy must overwrite or strip any Remote-User header the client sends.
services:
  poweradmin:
    image: poweradmin/poweradmin:latest
    environment:
      PA_REMOTE_USER_ENABLED: "true"
      PA_REMOTE_USER_HEADER: Remote-User
      PA_REMOTE_USER_TRUSTED_PROXIES: 172.20.0.5
      PA_REMOTE_USER_EMAIL_ATTRIBUTE: Remote-Email
      PA_REMOTE_USER_NAME_ATTRIBUTE: Remote-Name
      PA_REMOTE_USER_GROUPS_ATTRIBUTE: Remote-Groups
      PA_REMOTE_USER_GROUP_MAPPING: dns-admins=Administrators
      PA_REMOTE_USER_PERMISSION_TEMPLATE_MAPPING: dns-admins=Administrator
      PA_REMOTE_USER_LOGOUT_URL: https://auth.example.com/logout
    networks:
      app:
        ipv4_address: 172.20.0.10   # reachable only from the proxy network, no published ports

Custom CA Certificate

Variable Default Description
TRUSTED_CA_FILE - Path to a custom CA certificate file inside the container

Use this when connecting to services (OIDC, SAML, LDAP, PowerDNS API) that use self-signed or internal CA certificates:

docker run -d --name poweradmin -p 80:80 \
  -e TRUSTED_CA_FILE=/certs/my-ca.crt \
  -v /path/to/my-ca.crt:/certs/my-ca.crt:ro \
  poweradmin/poweradmin

Interface and miscellaneous

Variable Default Description
PA_CONFIG_PATH /app/config/settings.php Path to the configuration file. When this file exists and is not empty it replaces the generated one, and the other PA_* variables are ignored
PA_APP_TITLE Poweradmin Application title
PA_DEFAULT_LANGUAGE en_EN Default language
PA_STYLE light UI style: light or dark
PA_SESSION_TIMEOUT 1800 Session timeout in seconds
PA_TIMEZONE UTC Default timezone
PA_EDIT_CONFLICT_RESOLUTION last_writer_wins Edit conflict resolution strategy
PA_DNS_CUSTOM_TLDS - Comma-separated custom TLDs (e.g., dn42,home)

The interface has many more settings than the four above; see UI Overview for what is available and DOCKER.md for their variables.

Logging

Variable Default Description
PA_LOGGING_TYPE null Logger type: null or native
PA_LOGGING_LEVEL info Log level: debug, info, notice, warning, error, critical, alert, emergency
PA_LOGGING_DATABASE_ENABLED false Log zone/record changes to database
PA_LOGGING_SYSLOG_ENABLED false Log auth attempts to syslog
PA_LOGGING_SYSLOG_IDENTITY poweradmin Syslog program identity
PA_LOGGING_SYSLOG_FACILITY LOG_USER Syslog facility (LOG_USER, LOG_LOCAL0-LOG_LOCAL7)

Health endpoints (v4.5.0+)

Variable Default Description
PA_HEALTH_ENABLED false Unauthenticated readiness endpoint at /api/health
PA_HEALTH_PING_ENABLED false Unauthenticated liveness endpoint at /ping
PA_HEALTH_DB_TIMEOUT 2 Database connect timeout in seconds used by the health check
PA_HEALTH_PDNS_TIMEOUT 2 PowerDNS API timeout in seconds used by the health check

The image's HEALTHCHECK requests /ping when PA_HEALTH_PING_ENABLED=true (so a headless container with PA_WEB_ENABLED=false still reports healthy) and / otherwise. Both succeed even with a dead database. To have container status track real readiness, enable PA_HEALTH_ENABLED and override the healthcheck to request /api/health. See Health Checks.

Change approval (v4.6.0+)

Variable Default Description
PA_APPROVAL_ENABLED false Route zone changes of request-only users through review
PA_APPROVAL_REQUIRE_REVIEW_FOR_ALL false Every zone change becomes a change request, even for editors and admins
PA_APPROVAL_ALLOW_SELF_APPROVAL true Whether a requester may approve their own change request
PA_NOTIFICATION_CHANGE_REQUEST false Mail reviewers on new change requests and requesters on decisions; needs PA_MAIL_ENABLED
PA_NOTIFICATION_CHANGE_REQUEST_SOA_CONTACT false Also mail the zone's SOA contact about new change requests

See Change Requests.

API and CORS

Variable Default Description
CORS_ALLOW_ORIGIN * Access-Control-Allow-Origin sent on /api/* responses and preflight requests. The web interface never sends CORS headers

PHP settings

PHP runs on php.ini-production with these overrides:

Setting Value
memory_limit 256M
upload_max_filesize / post_max_size 16M / 20M
expose_php Off
opcache.validate_timestamps 0 (code is cached until the container restarts)

To change any of them, mount an ini file into /usr/local/etc/php/conf.d/. The image's own overrides load first, so any file you mount there wins.

PHP warnings go to the container log (docker logs), not into the page (v4.4.2+).

Anything not listed on this page is in DOCKER.md, which tracks the code and is tagged with each release.

Volumes

Path Description
/db SQLite database directory
/app/config Configuration files (optional)

Customizing the interface

Mount only the files you customize, never the whole /app/templates directory or /app. Templates change together with the code that renders them, so a templates directory from another Poweradmin version keeps serving outdated pages after an upgrade. That breaks forms in ways that are hard to trace, for example saving records failing with a PHP max_input_vars warning.

Custom CSS files are not shipped with the image, so mount them one by one:

docker run -d --name poweradmin -p 80:80 \
  -v ./custom_light.css:/app/templates/default/style/custom_light.css:ro \
  -v ./custom_dark.css:/app/templates/default/style/custom_dark.css:ro \
  poweradmin/poweradmin

Use modern instead of default in the path when PA_THEME is modern. Do not mount the whole style/ directory: it hides the theme's own light.css and dark.css.

A custom header or footer (/app/templates/<theme>/custom/header.html and footer.html) starts as a copy of the theme's own file, so compare it with the new version after each upgrade.

Secrets

For production, use Docker secrets instead of environment variables for sensitive data. See Docker Secrets for details.

secrets:
  db_password:
    file: ./secrets/db_password.txt

services:
  poweradmin:
    environment:
      DB_PASS__FILE: /run/secrets/db_password
    secrets:
      - db_password

Non-Root / Rootless Deployment

The Poweradmin image supports running as a non-root user for restricted Kubernetes clusters and OpenShift. No separate image variant is needed - the entrypoint adapts automatically.

Behavior

Start mode Port Privileges Use case
Root (default) 80 Drops to www-data after setup Standard Docker, unrestricted K8s
Non-root 8080 (auto) No chown/chmod/CA install Restricted K8s, OpenShift

Docker (Non-Root)

docker run --rm --user 82:82 -p 8080:8080 \
  -e DB_TYPE=sqlite \
  poweradmin/poweradmin:stable

Kubernetes (Restricted)

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 82
    runAsGroup: 82
    fsGroup: 82
  containers:
    - name: poweradmin
      image: poweradmin/poweradmin:stable
      ports:
        - containerPort: 8080
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop: ["ALL"]
      env:
        - name: DB_TYPE
          value: sqlite

fsGroup: 82 ensures volumes are group-writable for www-data (GID 82).

Custom Port

Override the auto-detected port with SERVER_PORT:

docker run --rm -e SERVER_PORT=9090 -p 9090:9090 poweradmin/poweradmin

Limitations (Non-Root)

  • TRUSTED_CA_FILE requires root - a warning is logged if set in non-root mode
  • Volumes must be pre-configured as writable (use fsGroup or host permissions)

Troubleshooting

Check container logs

docker logs poweradmin

Access container shell

docker exec -it poweradmin /bin/sh

Database connection issues

  1. Verify database is accessible from container
  2. Check credentials are correct
  3. Ensure database exists and user has permissions

Permission issues with volumes

docker run --user root ...
# or fix permissions on host
sudo chown -R 1000:1000 /path/to/volume