unbound
Minimal Alpine container with Unbound compiled against the latest OpenSSL 3.5 branch (ARM64 + AMD64)
1.9K
Unbound is compiled against the latest OpenSSL 3.5 branch at build time, and is compiled with the following flags which enables the HTTP/2 DoH client interface and allows for ECS if you need that: --with-libevent --with-libnghttp2 --enable-subnet
I have added some optimised images so for e.g. oci-arm64 is compiled for the OCI Ampere A1 CPU, but if you don't know the capabilities of your CPU the generic "latest" tag will work just fine.
You will want to make sure in the folder where your compose.yaml is located, you have a folder called config.
Inside the config folder, you will put your unbound.conf to configure Unbound how you like (add more ports in your compose.yaml as well if you are enabling DoH incoming for example). I am not giving sample unbound.conf because really you need to understand it and configure it for your specific needs (including tuning it for your hardware). See unbound configuration options here: https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html use them to build your unbound.conf, AI can help you and also assist with tuning to your hardware.
Then to run it all, in the config folder you need to place a script init.sh which is ran when the docker container starts. Make sure your init.sh conforms with syntax for sh NOT bash.
Ultimately you should have a folder structure like this:
unbound-docker
|_ compose.yaml
|_ config
|_ init.sh
|_ unbound.conf
|_ (other optional files like root.key, fullchain.pem, privkey.pem etc.)
Here is an example compose.yaml for pure DNS53 listening on all IP addresses on the host (Configure your Unbound upstream/forward for DoT or DoH):
services:
unbound:
restart: unless-stopped
image: farfoxachedocker/unbound:latest
ulimits:
nofile:
soft: 65886
hard: 65886
ports:
- 0.0.0.0:53:53/tcp
- 0.0.0.0:53:53/udp
- "[::]:53:53/tcp"
- "[::]:53:53/udp"
volumes:
- ./config:/config:ro
- ./config:/usr/local/etc/unbound:rw
- /etc/ssl/certs:/etc/ssl/certs:ro
networks:
default:
enable_ipv6: true
Here is an example init.sh you can use with a health check that restarts Unbound if it stops resolving for whatever reason:
#!/bin/sh
# =============================================================================
# init.sh - Unbound Docker Startup with Smart DNS Health Monitoring
# =============================================================================
echo "=== Unbound Container Starting: $(date) ==="
echo " "
echo "→ Starting Unbound..."
unbound -c /config/unbound.conf -d &
UNBOUND_PID=$!
# =============================================================================
# Background Health Monitor
# - Normal check: every 60 seconds
# - On failure: check every 15 seconds
# - After 3 consecutive failures → restart Unbound
# =============================================================================
echo "→ Starting DNS health monitor (nslookup google.com @127.0.0.1)..."
failure_count=0
check_interval=60
while true; do
sleep $check_interval
if nslookup google.com 127.0.0.1 > /dev/null 2>&1; then
# Success
if [ $failure_count -gt 0 ]; then
echo "✅ DNS health restored"
fi
failure_count=0
check_interval=60
else
# Failure
failure_count=$((failure_count + 1))
echo "⚠️ DNS check failed ($failure_count/3) - $(date)"
if [ $failure_count -ge 3 ]; then
echo "❌ 3 consecutive DNS failures - Restarting Unbound..."
kill -TERM $UNBOUND_PID 2>/dev/null || true
sleep 2
unbound -c /config/unbound.conf -d &
UNBOUND_PID=$!
failure_count=0
check_interval=60
echo "✅ Unbound restarted"
else
# Switch to fast checking
check_interval=15
fi
fi
done
Container is a very minimal Alpine image, further hardened as well. The container is built with unbound user and group, id of 1000 for both. I encourage you to insert username: "unbound" into your unbound.conf so Unbound drops root privileges and runs as a pleb user.
RUN: Make sure you cd into the unbound-docker folder and then sudo docker compose up -d.
UPDATE: Make sure you cd into the unbound-docker folder and then sudo docker compose pull && sudo docker compose down && sudo docker compose up -d
REMOVE: Make sure you cd into the unbound-docker folder and then sudo docker compose down && sudo docker image rm farfoxachedocker/unbound:latest && cd .. && sudo rm -rf unbound-docker
GLHF, contactable at [email protected]
Content type
Image
Digest
sha256:d3804dbb9…
Size
31 MB
Last updated
about 1 month ago
docker pull farfoxachedocker/unbound:u1.25.1-ossl3.5.7