PDNS Manager

Security & hardening

What setup does for you automatically and what you need to do yourself before exposing the panel.

What setup handles automatically

  • JWT_SECRET_KEY generated with openssl rand -hex 64 – without it every container restart invalidates all sessions.
  • DB passwords generated randomly.
  • .env gets chmod 600.
  • Swagger/OpenAPI at /docs is disabled by default (DOCS_ENABLED=false).
  • Password hashing via pwdlib + bcrypt. Old passlib hashes still work.
  • Login rate limit active since v2.3.7 (HTTP 429 after too many failures).
  • Webhook SSRF protection active since v2.3.7 (private URLs blocked).

What you have to do yourself

1 · Reverse proxy with TLS in front

The built-in HTTP server should not be reachable from the internet directly. Caddy is by far the fastest:

pdns.example.com {
    encode zstd gzip
    reverse_proxy localhost:5380
}

nginx (classic, no auto-HTTPS):

server {
    listen 443 ssl http2;
    server_name pdns.example.com;

    ssl_certificate     /etc/letsencrypt/live/pdns.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pdns.example.com/privkey.pem;

    client_max_body_size 8m;

    location / {
        proxy_pass         http://127.0.0.1:5380;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_read_timeout 60s;
    }
}

2 · Once HTTPS is up: turn on the cookie secure flag

# in .env
AUTH_COOKIE_SECURE=true

Then docker compose up -d once. Otherwise the browser sends the cookie over HTTPS unreliably.

3 · Disable self-registration once accounts are created

# in .env
ENABLE_REGISTRATION=false

4 · Firewall: port 5380 only locally

sudo ufw allow 22/tcp
sudo ufw allow 80,443/tcp
sudo ufw deny 5380/tcp
sudo ufw enable

Now only the local reverse proxy can reach the panel.

5 · Change the admin password after first login

If you used the initial password from the container log, set your own one in your profile right after first login – ideally enable TOTP too.

6 · Encrypt SMTP traffic

In the SMTP panel pick STARTTLS or TLS – not "no SSL". Otherwise mailserver credentials travel in clear text.

7 · DOCS_ENABLED on purpose

If you don't need the OpenAPI docs at /docs, leave DOCS_ENABLED=false. Endpoints work without it; otherwise every anonymous visitor would get the full schema.

Optional extras

  • Fail2Ban on reverse proxy logs (additional layer to the built-in rate limit).
  • IP allow-list for /api/v1/auth/login at the reverse proxy if you don't allow self-registration.
  • Cloudflare Tunnel / Tailscale Funnel – run the panel without open ports.
  • Backups: ./update.sh takes one before each update, but don't rely on that. Better: daily cron dump to S3/Backblaze.

Known limitations

  • No multi-tenant separation (no isolated "customers"). For that look at PowerDNS-Admin or commercial products.
  • No built-in reverse proxy / TLS – use Caddy / nginx / Traefik / Cloudflare Tunnel.
  • No DHCP, no recursor – this manages authoritative zones.

Found a vulnerability?

Don't open a public issue. Use GitHub Security → "Report a vulnerability" instead – details in SECURITY.md.