RpiDNS is a DNS firewall solution using ISC Bind9 for DNS resolution and RPZ-based blocking. This container deployment provides a lightweight, portable way to run RpiDNS using Docker.
The deployment consists of two containers:
┌─────────────────────────────────────────────────────────────────┐
│ Host System │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Docker Network: rpidns-net │ │
│ │ ┌─────────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ Bind Container │ │ Web Container │ │ │
│ │ │ ─────────────── │ │ ───────────────── │ │ │
│ │ │ ISC Bind9 + RPZ │ │ OpenResty + PHP-FPM │ │ │
│ │ │ Port 53 TCP/UDP │ │ + RSyslog │ │ │
│ │ │ │ │ Ports 80, 443, 10514 │ │ │
│ │ └─────────────────────┘ └─────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Persistent Volumes │ │
│ │ ./config/bind ./config/nginx ./www ./logs ./scripts│ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
- Base Image: Alpine Linux 3.24
- Purpose: DNS resolution with RPZ (Response Policy Zone) blocking
- Packages: bind, bind-tools, rsyslog, bash
- Base Image: Alpine Linux 3.24
- Purpose: Web UI for RpiDNS management, log collection
- Packages: openresty, php84-fpm, php84-sqlite3, rsyslog, dcron, git, openssl
mkdir -p rpidns
cd rpidns
mkdir -p config/bind config/nginx www www/db logs scripts bind-cacheCopy the docker-compose.yml from this repository to your deployment directory.
Create a .env file:
RPIDNS_HOSTNAME=rpidns.local
RPIDNS_DNS_TYPE=primary
RPIDNS_DNS_IPNET=192.168.0.0/16
RPIDNS_LOGGING=local
RPIDNS_LOGGING_HOST=docker-compose up -d# Check container status
docker-compose ps
# Test DNS resolution
dig @localhost example.com
# Access web UI
open http://localhost| Variable | Default | Description |
|---|---|---|
RPIDNS_HOSTNAME |
rpidns.local |
Hostname for the RpiDNS instance |
RPIDNS_DNS_TYPE |
primary |
DNS server type: primary or secondary |
RPIDNS_DNS_IPNET |
192.168.0.0/16 |
IP network for DNS ACL (allowed query sources) |
RPIDNS_LOGGING |
local |
Logging mode: local or forward |
RPIDNS_LOGGING_HOST |
(empty) | Remote syslog host (when RPIDNS_LOGGING=forward) |
| Variable | Default | Description |
|---|---|---|
RPIDNS_HOSTNAME |
rpidns.local |
Hostname for the RpiDNS instance |
RPIDNS_LOGGING |
local |
Logging mode: local or forward |
RPIDNS_LOGGING_HOST |
(empty) | Remote syslog host (when RPIDNS_LOGGING=forward) |
PHP_FPM_VERSION |
84 |
PHP-FPM version (default: PHP 8.4) |
RPIDNS_ADMIN_PASSWORD |
(auto-generated) | Admin password for web UI |
| Container Path | Host Path | Description |
|---|---|---|
/etc/bind |
./config/bind |
Bind configuration files (named.conf, zone files) |
/var/cache/bind |
./bind-cache |
Zone data cache and dynamic updates |
/opt/rpidns/logs |
./logs |
DNS query logs |
| Container Path | Host Path | Description |
|---|---|---|
/opt/rpidns/conf |
./config/nginx |
Nginx/OpenResty configuration, SSL certificates |
/opt/rpidns/www |
./www |
Web application files |
/opt/rpidns/www/db |
./www/db |
SQLite database (persistent) |
/opt/rpidns/logs |
./logs |
Application and DNS logs |
/opt/rpidns/scripts |
./scripts |
Maintenance scripts |
| Port | Protocol | Container | Description |
|---|---|---|---|
| 53 | TCP/UDP | Bind | DNS queries |
| 80 | TCP | Web | HTTP web interface |
| 443 | TCP | Web | HTTPS web interface |
| 10514 | TCP | Web | Syslog receiver (for remote RpiDNS instances) |
In local mode, the web container's RSyslog listens on port 10514 to receive logs from remote RpiDNS instances. Logs are written to:
/opt/rpidns/logs/bind_<source-ip>_queries.log
This is useful when running a central RpiDNS server that collects logs from multiple remote instances.
In forward mode, the bind container forwards its DNS query logs to an external syslog server specified by RPIDNS_LOGGING_HOST. This is useful when:
- Running RpiDNS on edge devices
- Centralizing logs to a SIEM or log management system
Example configuration:
RPIDNS_LOGGING=forward
RPIDNS_LOGGING_HOST=192.168.1.100The web container automatically generates SSL certificates on first startup:
- Server Certificate: Self-signed certificate for HTTPS
- CA Certificate: Used for dynamic SSL certificate generation
- Intermediate Certificate: For certificate chain
- Fallback Certificate: Used when dynamic generation fails
Certificates are stored in /opt/rpidns/conf/ssl and /opt/rpidns/conf/ssl_sign.
To use your own certificates, mount them to:
/opt/rpidns/conf/ssl/server.key/opt/rpidns/conf/ssl/server.crt
Both containers include health checks:
dig @127.0.0.1 localhost +short +time=2 +tries=1- Interval: 30s
- Timeout: 10s
- Retries: 3
wget -q --spider http://127.0.0.1/blocked.php- Interval: 30s
- Timeout: 10s
- Retries: 3
The web container runs scheduled maintenance tasks:
| Schedule | Task |
|---|---|
| Daily | Clean SSL certificate cache (remove certs older than 30 days) |
| Daily | Clean unused SSL certificates (remove after 7 days of inactivity) |
Logs in /opt/rpidns/logs should be rotated using host-level logrotate or a similar tool.
# Bind container logs
docker logs rpidns-bind
# Web container logs
docker logs rpidns-web# Query the local DNS server
dig @localhost example.com
# Check if RPZ blocking is working
dig @localhost known-malicious-domain.comdocker inspect --format='{{.State.Health.Status}}' rpidns-bind
docker inspect --format='{{.State.Health.Status}}' rpidns-web-
Port 53 already in use: Stop any existing DNS services (systemd-resolved, dnsmasq)
sudo systemctl stop systemd-resolved
-
Permission denied on volumes: Ensure host directories have correct permissions
sudo chown -R 82:82 ./www ./logs sudo chown -R 100:101 ./bind-cache
-
Web container fails health check: Check PHP-FPM is running
docker exec rpidns-web ps aux | grep php-fpm
If you need to build the images locally instead of using pre-built images:
# Build Bind container
docker build -t rpidns-bind:local ./bind
# Build Web container
docker build -t rpidns-web:local ./webThen update docker-compose.yml to use local images:
services:
bind:
image: rpidns-bind:local
web:
image: rpidns-web:localRpiDNS is open source software. See the main repository for license details.