Sign inSign up

bfren/unbound

By bfren

โ€ขUpdated 8 minutes ago

Image
0

10K+

bfren/unbound repository overview

โ Unbound

GitHub release (latest by date) Docker Pulls Docker Image Size GitHub Workflow Status

Docker Hubโ  - Quay.ioโ  - bfren ecosystemโ 

โ Introduction

Unboundโ  is a recursive DNS resolver: instead of sending your lookups to a public DNS provider, it asks the root servers and each domain's own name servers directly, and can check the answers with DNSSEC.

It is built on Alpine + S6โ , with Unbound from Alpine's packages, and is configured with environment variables. It works well as the upstream resolver for Pi-holeโ  - see the example compose files in this repository.

โ Quickstart

services:
  unbound:
    image: quay.io/bfren/unbound:3.1
    restart: unless-stopped
    ports:
      - "127.0.0.1:53:53/tcp"
      - "127.0.0.1:53:53/udp"
    environment:
      - BF_UNBOUND_ENABLE_DNSSEC=1

Run docker compose up -d, then test it from the host:

dig example.com @127.0.0.1

docker-compose.yml in this repository runs Pi-hole with two Unbound containers, and docker-compose-with-proxy.yml puts Pi-hole's admin site behind a proxy. Copy .env alongside them and change the values.

โ Features

  • ๐Ÿ”Ž Recursive resolver - no third-party DNS provider sees your lookups
  • ๐Ÿ” DNSSEC validation, with the root trust anchor kept up to date
  • โš™๏ธ About 50 Unbound settings available as environment variables
  • ๐Ÿ›ก๏ธ Optional protection against DNS rebinding (private addresses)
  • โœ… Configuration checked before Unbound starts
  • ๐Ÿฉบ Health check that resolves a real domain
  • ๐Ÿงช Built-in tests

โ Tags

Images are published to Docker Hub (bfren/unbound) and Quay.io (quay.io/bfren/unbound) for linux/amd64, linux/arm/v7 and linux/arm64, with Unbound 1.25.2 on Alpine 3.24.

TagExampleUse when
latestYou always want the newest image
MAJOR3You want updates, but not breaking changes
MINOR3.1You want fixes only - recommended
VERSION3.1.16You want to pin this image exactly
unboundX, unboundX.Yunbound1.25You want an Unbound version
unboundX.Y.Zunbound1.25.2You want an exact Unbound release
unboundX.Y-VERSIONunbound1.25-3.1.16You want to pin both

โ Configuration

โ Ports
PortPurpose
53/udpDNS
53/tcpDNS (large answers and DNS over TCP)
โ Environment Variables
VariableValuesDescriptionDefault
BF_UNBOUND_ENABLE_DNSSEC0, 1Validate answers with DNSSEC, using the root trust anchor (downloaded and kept up to date). Recommended.0
BF_UNBOUND_PRIVATE_ADDRESS_ENABLE0, 1Remove private addresses (e.g. 192.168.0.0/16) from public answers, to protect against DNS rebinding.0

Each of these sets the Unbound option of the same name - see the unbound.conf manualโ  for what they do:

VariableUnbound optionDefault
BF_UNBOUND_VERBOSITYverbosity1
BF_UNBOUND_STATISTICS_INTERVALstatistics-interval0
BF_UNBOUND_SHM_ENABLEshm-enableno
BF_UNBOUND_SHM_KEYshm-key11777
BF_UNBOUND_STATISTICS_CUMULATIVEstatistics-cumulativeno
BF_UNBOUND_EXTENDED_STATISTICSextended-statisticsno
BF_UNBOUND_STATISTICS_INHIBIT_ZEROstatistics-inhibit-zeroyes
BF_UNBOUND_NUM_THREADSnum-threads1
BF_UNBOUND_INTERFACE_AUTOMATICinterface-automaticyes
BF_UNBOUND_PREFER_IP6prefer-ip6no
BF_UNBOUND_PREFER_IP4prefer-ip4no
BF_UNBOUND_OUTGOING_NUM_TCPoutgoing-num-tcp10
BF_UNBOUND_INCOMING_NUM_TCPincoming-num-tcp10
BF_UNBOUND_SO_RCVBUFso-rcvbuf0
BF_UNBOUND_SO_SNDBUFso-sndbuf0
BF_UNBOUND_EDNS_BUFFER_SIZEedns-buffer-size1232
BF_UNBOUND_MAX_UDP_SIZEmax-udp-size1232
BF_UNBOUND_SCRUB_PROMISCUOUSiter-scrub-promiscuousyes
BF_UNBOUND_DISCARD_TIMEOUTdiscard-timeout1900
BF_UNBOUND_WAIT_LIMITwait-limit1000
BF_UNBOUND_WAIT_LIMIT_COOKIEwait-limit-cookie10000
BF_UNBOUND_CACHE_MIN_TTLcache-min-ttl0
BF_UNBOUND_CACHE_MAX_TTLcache-max-ttl86400
BF_UNBOUND_CACHE_MIN_NEGATIVE_TTLcache-min-negative-ttl0
BF_UNBOUND_DO_IP4do-ip4yes
BF_UNBOUND_DO_IP6do-ip6yes
BF_UNBOUND_DO_UDPdo-udpyes
BF_UNBOUND_DO_TCPdo-tcpyes
BF_UNBOUND_HIDE_IDENTITYhide-identityno
BF_UNBOUND_HIDE_VERSIONhide-versionno
BF_UNBOUND_HIDE_HTTP_USER_AGENThide-http-user-agentno
BF_UNBOUND_HARDEN_SHORT_BUFSIZEharden-short-bufsizeyes
BF_UNBOUND_HARDEN_LARGE_QUERIESharden-large-queriesno
BF_UNBOUND_HARDEN_GLUEharden-glueyes
BF_UNBOUND_HARDEN_UNVERIFIED_GLUEharden-unverified-glueyes
BF_UNBOUND_HARDEN_DNS_STRIPPEDharden-dnssec-strippedno
BF_UNBOUND_HARDEN_BELOW_NXDOMAINharden-below-nxdomainno
BF_UNBOUND_HARDEN_ALGO_DOWNGRADEharden-algo-downgradeno
BF_UNBOUND_HARDEN_UNKNOWN_ADDITIONALharden-unknown-additionalno
BF_UNBOUND_QNAME_MINIMISATIONqname-minimisationyes
BF_UNBOUND_AGGRESSIVE_NSECaggressive-nsecyes
BF_UNBOUND_USE_CAPS_FOR_IDuse-caps-for-idno
BF_UNBOUND_UNWANTED_REPLY_THRESHOLDunwanted-reply-threshold10000000
BF_UNBOUND_DO_NOT_QUERY_LOCALHOSTdo-not-query-localhostyes
BF_UNBOUND_PREFETCHprefetchno
BF_UNBOUND_PREFETCH_KEYprefetch-keyno
BF_UNBOUND_DENY_ANYdeny-anyno
BF_UNBOUND_MINIMAL_RESPONSESminimal-responsesyes
BF_UNBOUND_EDEedeyes
BF_UNBOUND_EDE_SERVE_EXPIREDede-serve-expiredyes

See Alpine + S6โ  for BF_DEBUG, BF_TZ and the other base image variables.

โ How It Works

On every start /etc/unbound/unbound.conf is generated from the environment variables. Unbound listens on port 53 on every IPv4 address in the container, and answers anyone who can reach it - so only publish the port where you mean to.

Before Unbound starts:

  • with BF_UNBOUND_ENABLE_DNSSEC=1, unbound-anchor downloads or updates the root trust anchor (/etc/unbound/root.key)
  • unbound-checkconf checks the configuration - if it is invalid, Unbound doesn't start, and the error is in the log

Then Unbound runs under S6 as the unbound user. The container stops if it crashes. The root hints (the addresses of the root servers) are downloaded when the image is built.

The health check (every 30 seconds) looks up cloudflare.com using Unbound itself, so it fails if Unbound can't reach the internet.

โ Testing

./test.nu   # builds the image and runs /test
./run.sh    # builds the image and opens a shell, with DNSSEC on and Unbound on 127.0.0.1:5353/udp

/test runs this image's tests alongside the base images' tests: the generated unbound.conf (with and without DNSSEC, defaults, settings and private addresses), the paths used, and the installed Unbound version.

โ Troubleshooting

โ The container keeps restarting

Unbound's configuration check failed, or the trust anchor couldn't be downloaded. Check docker logs <container> - a mistyped value in a BF_UNBOUND_* variable is the usual cause.

โ Lookups fail with SERVFAIL

With DNSSEC on, SERVFAIL usually means an answer failed validation - often because the container's clock is wrong, or the domain's DNSSEC is broken. Try the lookup with +cd (checking disabled), e.g. dig example.com @127.0.0.1 +cd, to see which.

โ Port 53 is already in use on the host

On many Linux systems systemd-resolved uses port 53. Publish Unbound on a specific address (e.g. "192.168.1.10:53:53/udp"), or use it only from other containers.

โ FAQs

โ Why run two Unbound containers in the example?

So DNS keeps working while one restarts or updates. Pin the second one to an older image version (UNBOUND_PIN in .env) and update it separately.

โ Can I add my own Unbound settings?

unbound.conf includes every *.conf file in /etc/unbound/unbound.conf.d, so mount your own files there.

โ Licence

Unbound is (c) 2007, NLnet Labs and licensed under BSD-3โ .

Copyright (c) 2023-2026 bfrenโ  (unless otherwise stated)

Tag summary

Content type

Image

Digest

sha256:c09443e7eโ€ฆ

Size

29.9 MB

Last updated

9 minutes ago

docker pull bfren/unbound