In Docker, containers can typically access each other by their container names (relying on Docker's built-in DNS at 127.0.0.11). However, the host machine cannot resolve container names by default.
This tool —— the Docker DNS Forwarder , solves this problem: it allows the host to access container services directly using domain names in the format container-name.custom-suffix, no port mapping required, no hosts file modification needed.
By intercepting domain names that end with a custom suffix (e.g., .docker) and forwarding them to Docker’s built-in DNS server, the host can access containers seamlessly.
Example:
curl http://myapp.docker:8080The default suffix can be modified via command-line arguments or environment variables.
- Domains matching 
.docker→ Forwarded to Docker’s built-in DNS (127.0.0.11) for container IP resolution. - All other domains → Return 
REFUSED, so the host automatically uses public DNS (no impact on normal internet access). 
The default forwarding address can be modified via command-line arguments or environment variables.
A default domain gateway.docker is provided, which automatically resolves to the host machine’s IP.
The default gateway name can be modified via command-line arguments or environment variables.
Built on the ldns library and UDP protocol, it runs with minimal CPU and memory usage.
Docker’s built-in DNS forwards non-container-name domains to the host’s DNS service. This means you can also use domains like gateway.docker inside Docker containers:
Docker forwards the request to the host’s configured DNS server (i.e., this forwarder). The forwarder strips the .docker suffix to form a valid container name, then uses Docker’s built-in DNS to resolve the IP and return the result.
- 
Docker’s default bridge network (docker0) does not support inter-container access via container names (only via IP).
Use a custom network (created with
docker network create) to enable normal container name resolution.
For details on Docker custom networks, refer to the official Docker documentation. - 
Domain name queries are case-insensitive.
 
graph TD
    A[Host Initiates DNS Query] --> B{Does the domain end with .docker?}
    B -->|Yes| C[Strip .docker suffix → Forward to 127.0.0.11]
    C --> D[Return Container IP to Host]
    B -->|No| E[Return REFUSED]
    E --> F[Host Attempts Public DNS Resolution]
    | Solution | Base Image | Core Features | Image Size | Build Script | 
|---|---|---|---|---|
static | 
scratch (empty) | 
Contains only static binaries (no redundant system components) | Binary < 1.5MB | build-static.sh | 
alpine | 
alpine:3.22 | 
Lightweight Linux distro + runtime libraries + binaries | Binary ≈ 40kB + Image ≈ 9MB | build-alpine.sh | 
- Download or clone the repository.
 - Run the build script: 
build-alpine.shorbuild-static.sh. - Run the startup script 
docker-run.shand follow the prompts to complete initialization and deployment. 
- 
Build the image and run the container:
# Clone the source code git clone https://github.com/bytesharky/docker-dns # Domestic mirror (China): # git clone https://gitee.com/bytesharky/docker-dns cd docker-dns docker build -t docker-dns:static . # Start the container # Mount timezone data (optional: for local time in logs) # Set log level (optional: default is INFO) docker run -d \ -e LOG_LEVEL=INFO \ -e TZ=/zoneinfo/Asia/Shanghai \ -v /usr/share/zoneinfo:/zoneinfo:ro \ --network docker-net \ --name docker-dns-a \ -p 53:53/udp \ --restart always \ docker-dns:static
 - 
Configure Host DNS
Edit/etc/resolv.confand set127.0.0.1as the top DNS server:nameserver 127.0.0.1 # Local forwarder nameserver 223.5.5.5 # Public DNS 1 nameserver 8.8.8.8 # Public DNS 2(Optional) Prevent the file from being overwritten by the system:
sudo chattr +i /etc/resolv.conf
 
- 
Pull the prebuilt image:
docker pull ccr.ccs.tencentyun.com/sharky/docker-dns:static docker tag ccr.ccs.tencentyun.com/sharky/docker-dns:static docker-dns:static # Start the container # Mount timezone data (optional: for local time in logs) # Set log level (optional: default is INFO) docker run -d \ -e LOG_LEVEL=INFO \ -e TZ=/zoneinfo/Asia/Shanghai \ -v /usr/share/zoneinfo:/zoneinfo:ro \ --network docker-net \ --name docker-dns \ -p 53:53/udp \ --restart always \ docker-dns:static
 - 
Configure Host DNS
Refer to Step 2 in Method 2. 
ping -c 3 docker-dns.dockerExpected output (IP = container’s internal network address):
PING docker-dns.docker (172.18.0.6): 56 data bytes
64 bytes from 172.18.0.6: icmp_seq=1 ttl=64 time=0.05 msping -c 3 github.amrom.workers.devExpected output (public IP):
PING github.com (140.82.112.4): 56 data bytes
64 bytes from 140.82.112.4: icmp_seq=1 ttl=51 time=10.2 msControl the program’s output by setting the LOG_LEVEL environment variable (default: INFO).
Supported levels: DEBUG, INFO, WARN, ERROR, FATAL
| Number | Log Level | Core Meaning | Severity Level | 
|---|---|---|---|
| 0 | DEBUG | Debug level: Prints detailed runtime info for development/testing (aids in code debugging) | Lowest (only for dev environments) | 
| 1 | INFO | Info level: Records key statuses of normal system operation | Low (safe for production; logs normal events) | 
| 2 | WARN | Warning level: Records non-fatal exceptions or potential risks (system continues running) | Medium (monitor; may predict future issues) | 
| 3 | ERROR | Error level: Records fatal exceptions (single DNS resolution fails, but system remains functional) | High (investigate promptly to avoid scope expansion) | 
| 4 | FATAL | Fatal level: Records critical errors that render the system completely inoperable | Highest (system unavailable; urgent fix required) | 
The Docker DNS Forwarder acts as a "bridge" between the host machine and Docker’s built-in DNS. Its key advantages:
- 🟢 Host resolves 
.dockerdomains seamlessly - 🟢 No impact on normal internet DNS resolution
 - 🟢 Simple deployment + minimal resource usage
 
Development/testing environments, or scenarios where the host needs direct access to containers via container names.
Use with caution in production environments — fixed IP addresses are recommended instead.
| Short Opt | Long Option | Env Variable | Description | Default Value | 
|---|---|---|---|---|
-L | 
--log-level | 
LOG_LEVEL | 
Sets log verbosity (controls detail of output) | INFO | 
-G | 
--gateway | 
GATEWAY_NAME | 
Sets gateway name (inside Docker, the gateway is the host; allows resolving the host IP via gateway-name.suffix) | 
gateway | 
-S | 
--suffix | 
SUFFIX_DOMAIN | 
Sets the domain suffix for forwarded DNS queries | .docker | 
-C | 
--container | 
CONTAINER_NAME | 
Sets container name (used to send a test container-name.suffix resolution request to the forwarder on startup) | 
docker-dns | 
-D | 
--dns-server | 
FORWARD_DNS | 
Sets the target DNS server for forwarded queries (default: Docker’s built-in DNS) | 127.0.0.11 | 
-P | 
--port | 
LISTEN_PORT | 
Sets the port the service listens on | 53 | 
-K | 
--keep-suffix | 
KEEP_SUFFIX | 
Controls whether to retain the suffix when forwarding DNS queries (strip suffix when forwarding to 127.0.0.11) | 
Disabled | 
-M | 
--max-hops | 
MAX_HOPS | 
Sets maximum hop count for DNS queries (prevents looped queries) | 3 | 
-W | 
--workers | 
NUM_WORKERS | 
Sets the number of worker threads for the service | 4 | 
-f | 
--foreground | 
- | Runs the service in foreground mode (does not daemonize) | Disabled (daemon by default) | 
-h | 
--help | 
- | Shows this help message (lists options + descriptions) and exits | - | 
root@VM-4-2-debian:~# ./docker-dns/docker-dns -h
Usage: ./docker-dns [OPTIONS]
Options:
  -L, --log-level    Set log level (DEBUG, default: INFO, WARN, ERROR, FATAL)
  -G, --gateway      Set gateway name (default: gateway)
  -S, --suffix       Set suffix name (default: .docker)
  -C, --container    Set container name (default: docker-dns)
  -D, --dns-server   Set forward DNS server (default: 127.0.0.11)
  -P, --port         Set listening port (default: 53)
  -K, --keep-suffix  keep suffix forward dns query (default: strip)
  -M, --max-hops     Set maximum hop count (default: 3)
  -W, --workers      Set number of worker threads (default: 4)
  -f, --foreground   Run in foreground mode (do not daemonize)
  -h, --help         Show this help message and exit
Environment variable:
  Command-line arguments take precedence over environment variables.
  --log-level    =>  LOG_LEVEL
  --gateway      =>  GATEWAY_NAME
  --suffix       =>  SUFFIX_DOMAIN
  --container    =>  CONTAINER_NAME
  --dns-server   =>  FORWARD_DNS
  --port         =>  LISTEN_PORT
  --keep-suffix  =>  KEEP_SUFFIX
  --max-hops     =>  MAX_HOPS
  --workers      =>  NUM_WORKERS