Docker Installation
Poweradmin provides official Docker images for easy deployment with FrankenPHP.
Docker Images
Official images are available at:
- Docker Hub:
poweradmin/poweradmin - GitHub Container Registry:
ghcr.io/poweradmin/poweradmin
Image Tags
The image tags published for production and development use are:
| Tag | Source branch | Description |
|---|---|---|
stable |
release/4.2.x |
Tracks the latest tagged release on the stable line - recommended for production. |
4.2.x |
release/4.2.x |
Stable line; updates on every commit to the branch (more frequent than stable). |
4.3.x |
release/4.3.x |
Newer release line, still stabilizing. |
latest |
master |
Tracks master, which currently holds the next minor release in progress. |
dev |
develop |
Development tip - not for production. |
lts |
release/3.x |
Long-term support for the 3.x series. |
v* |
Tagged release | Pin to a specific version (e.g. v4.2.3). |
Note: The
nexttag was removed when the release branch structure changed. Thestableand per-version (v*) tags are the safest choices for production; the branch tags (4.2.x,4.3.x) update on every push and may include unreleased fixes.
Quick Start
SQLite (Default)
docker run -d --name poweradmin -p 80:80 \
-e PA_CREATE_ADMIN=1 \
-v poweradmin-db:/db \
poweradmin/poweradmin:stable
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 "Database prerequisites (MySQL/PostgreSQL)"
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"
environment:
PDNS_gmysql_host: mysql
PDNS_gmysql_user: pdns
PDNS_gmysql_password: pdns-password
PDNS_gmysql_dbname: pdns
PDNS_api: "yes"
PDNS_api_key: your-api-key
PDNS_webserver: "yes"
PDNS_webserver_address: "0.0.0.0"
PDNS_webserver_allow_from: "0.0.0.0/0"
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:
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
Database
| Variable | Default | Description |
|---|---|---|
DB_TYPE |
sqlite | Database type: sqlite, mysql, pgsql |
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) |
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 |
Security
| Variable | Default | Description |
|---|---|---|
PA_SESSION_KEY |
(auto) | Session encryption key |
PA_PASSWORD_ENCRYPTION |
bcrypt | Password hashing: bcrypt, argon2i, argon2id |
PA_LOGIN_TOKEN_VALIDATION |
true | Enable CSRF token validation for login |
PA_GLOBAL_TOKEN_VALIDATION |
true | Enable CSRF token validation for all forms |
PA_MFA_ENABLED |
false | Enable multi-factor authentication |
PA_MFA_ENFORCED |
false | Enforce MFA for users with enforce permission |
PA_RECAPTCHA_ENABLED |
false | Enable reCAPTCHA on login |
PA_RECAPTCHA_VERSION |
v3 | reCAPTCHA version: v2 or v3 |
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_PASSWORD_RESET_ENABLED |
false | Enable password reset functionality |
PA_USERNAME_RECOVERY_ENABLED |
false | Enable username recovery functionality |
Interface
| Variable | Default | Description |
|---|---|---|
PA_APP_TITLE |
Poweradmin | Application title |
PA_DEFAULT_LANGUAGE |
en_EN | Default language |
PA_SESSION_TIMEOUT |
1800 | Session timeout (seconds) |
PA_STYLE |
light | UI style: light or dark |
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_RDAP_ENABLED |
false | Enable RDAP lookup module |
PA_MODULE_RDAP_RESTRICT_TO_ADMIN |
true | Restrict RDAP to administrators |
PA_MODULE_EMAIL_PREVIEWS_RESTRICT_TO_ADMIN |
true | Restrict email previews to administrators |
PA_MODULE_DNS_WIZARDS_TYPES |
DMARC,SPF,DKIM,CAA,TLSA,SRV | Comma-separated list of DNS wizard types |
For detailed module configuration, see the Configuration section.
Authentication
| Variable | Default | Description |
|---|---|---|
PA_LDAP_ENABLED |
false | Enable LDAP authentication |
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 |
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
Miscellaneous
| Variable | Default | Description |
|---|---|---|
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) |
Logging
| Variable | Default | Description |
|---|---|---|
PA_LOGGING_TYPE |
null | Logger type: null or native |
PA_LOGGING_LEVEL |
info | Log level (debug, info, warning, error, etc.) |
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) |
For complete environment variable reference, see the DOCKER.md in the source repository.
Volumes
| Path | Description |
|---|---|
/db |
SQLite database directory |
/app/config |
Configuration files (optional) |
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_FILErequires root - a warning is logged if set in non-root mode- Volumes must be pre-configured as writable (use
fsGroupor host permissions)
Troubleshooting
Check container logs
docker logs poweradmin
Access container shell
docker exec -it poweradmin /bin/sh
Database connection issues
- Verify database is accessible from container
- Check credentials are correct
- 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