farfoxachedocker/unbound

By farfoxachedocker

Updated about 1 month ago

Minimal Alpine container with Unbound compiled against the latest OpenSSL 3.5 branch (ARM64 + AMD64)

Image
Networking
0

1.9K

farfoxachedocker/unbound repository overview

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]

Tag summary

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