networks: frontend: name: traefik external: true backend: internal: true edge: services: db: image: postgres:16 container_name: pdns-db environment: POSTGRES_DB: postgres POSTGRES_USER: ${PGUSER:?missing PGUSER} POSTGRES_PASSWORD: ${PGPASSWORD:?missing PGPASSWORD} TZ: ${TZ:-Asia/Shanghai} PGTZ: ${TZ:-Asia/Shanghai} volumes: # Keep the existing mount path to avoid moving the current data directory. - dbdata:/var/lib/postgresql - ./db-init-generated:/docker-entrypoint-initdb.d:ro - ./backup:/backup:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] interval: 10s timeout: 5s retries: 10 restart: unless-stopped networks: [backend, edge] auth: image: powerdns/pdns-auth-50:5.0.6 container_name: pdns-auth depends_on: db: condition: service_healthy ports: - "53:53/udp" - "53:53/tcp" - "127.0.0.1:8081:8081" environment: PDNS_API_KEY: ${PDNS_API_KEY:?missing PDNS_API_KEY} DB_NAME: ${DB_NAME:?missing DB_NAME} DB_USER: ${DB_USER:?missing DB_USER} DB_PASS: ${DB_PASS:?missing DB_PASS} TEMPLATE_FILES: secrets volumes: - ./auth/pdns.conf:/etc/powerdns/pdns.conf:ro - ./auth/templates.d:/etc/powerdns/templates.d:ro - ./auth/keys:/var/lib/powerdns - ./auth/import:/import - ./auth/export:/export - ./auth/logs:/var/log/pdns healthcheck: test: [ "CMD-SHELL", "python3 -c \"import json, os, urllib.request; req = urllib.request.Request('http://127.0.0.1:8081/api/v1/servers/localhost', headers={'X-API-Key': os.environ['PDNS_API_KEY']}); data = json.load(urllib.request.urlopen(req, timeout=3)); assert data['daemon_type'] == 'authoritative'\"" ] interval: 10s timeout: 5s retries: 12 restart: unless-stopped networks: [backend, edge] poweradmin: image: poweradmin/poweradmin:stable container_name: poweradmin depends_on: db: condition: service_healthy auth: condition: service_healthy environment: DB_TYPE: pgsql DB_HOST: ${DB_HOST:-db} DB_PORT: ${DB_PORT:-5432} DB_NAME: ${DB_NAME:?missing DB_NAME} DB_USER: ${DB_USER:?missing DB_USER} DB_PASS: ${DB_PASS:?missing DB_PASS} PA_PDNS_API_URL: http://auth:8081 PA_PDNS_API_KEY: ${PDNS_API_KEY:?missing PDNS_API_KEY} PA_DNS_BACKEND: sql PDNS_VERSION: ${PDNS_VERSION:-50} DNS_NS1: ${DNS_NS1:-ns1.wsvc.info} DNS_NS2: ${DNS_NS2:-ns2.wsvc.info} DNS_HOSTMASTER: ${DNS_HOSTMASTER:-hostmaster.wsvc.info} PA_APP_TITLE: ${PA_APP_TITLE:-Poweradmin} PA_TIMEZONE: ${TZ:-Asia/Shanghai} PA_SESSION_KEY: ${PA_SESSION_KEY:?missing PA_SESSION_KEY} PA_CREATE_ADMIN: ${PA_CREATE_ADMIN:-1} PA_ADMIN_USERNAME: ${PA_ADMIN_USERNAME:?missing PA_ADMIN_USERNAME} PA_ADMIN_PASSWORD: ${PA_ADMIN_PASSWORD:?missing PA_ADMIN_PASSWORD} PA_ADMIN_EMAIL: ${PA_ADMIN_EMAIL:?missing PA_ADMIN_EMAIL} PA_ADMIN_FULLNAME: ${PA_ADMIN_FULLNAME:?missing PA_ADMIN_FULLNAME} TRUSTED_PROXIES: private_ranges DEBUG: "false" restart: unless-stopped networks: [backend, frontend] labels: - "traefik.enable=true" - "traefik.docker.network=traefik" - "traefik.http.routers.poweradmin.rule=Host(`pdns.wsvc.info`)" - "traefik.http.routers.poweradmin.entrypoints=websecure" - "traefik.http.routers.poweradmin.tls.certresolver=letsencrypt" - "traefik.http.services.poweradmin.loadbalancer.server.port=80" backup: # Use postgres:16 so bash/pg_dump/flock exist without runtime package installs. # backend is internal:true — Alpine apk at start cannot reach mirrors. image: postgres:16 container_name: pdns-backup depends_on: db: condition: service_healthy environment: TZ: ${TZ:-Asia/Shanghai} DB_HOST: ${DB_HOST:-db} DB_PORT: ${DB_PORT:-5432} DB_USER: ${PGUSER:?missing PGUSER} DB_PASS: ${PGPASSWORD:?missing PGPASSWORD} DB_NAME: ${DB_NAME:?missing DB_NAME} RETENTION_DAYS: ${RETENTION_DAYS:-7} MAX_BACKUPS: ${MAX_BACKUPS:-7} DUMP_ROLES: ${DUMP_ROLES:-true} CRON_SCHEDULE: ${CRON_SCHEDULE:?missing CRON_SCHEDULE} volumes: - ./backup:/backup - ./scripts:/scripts:ro entrypoint: ["/bin/bash", "/scripts/backup-scheduler.sh"] restart: unless-stopped networks: [backend] pgweb: image: sosedoff/pgweb:0.16.2 container_name: pdns_pgweb restart: unless-stopped environment: PGWEB_DATABASE_URL: "postgres://${PGUSER:?missing PGUSER}:${PGPASSWORD:?missing PGPASSWORD}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:?missing DB_NAME}?sslmode=disable" PGWEB_AUTH_USER: ${PGWEB_USER:?missing PGWEB_USER} PGWEB_AUTH_PASS: ${PGWEB_PASS:?missing PGWEB_PASS} TZ: ${TZ:-Asia/Shanghai} depends_on: db: condition: service_healthy networks: [backend, frontend] labels: - "traefik.enable=true" - "traefik.docker.network=traefik" - "traefik.http.routers.pgweb.rule=Host(`pgweb.wsvc.info`)" - "traefik.http.routers.pgweb.entrypoints=websecure" - "traefik.http.routers.pgweb.tls.certresolver=letsencrypt" - "traefik.http.services.pgweb.loadbalancer.server.port=8081" volumes: dbdata: {}