How to Configure a Local DNS Domain for Containers to Access Host Services
Use sudo container system dns create <domain> --localhost <ip> to map a custom hostname to a specific IPv4 address, enabling containers to resolve and connect to host-bound services via the embedded DNS server.
The apple/container project ships with an embedded DNS server that bridges networking between containers and the host system. Configuring a local DNS domain allows you to expose host services to containers using human-readable hostnames rather than hardcoded IP addresses, with automatic resolution handled by the LocalhostDNSHandler component.
Select a Safe IP Address Range
Before creating a DNS entry, choose an IPv4 address that will not conflict with actual network infrastructure. The source code recommends using documentation blocks or private ranges that are guaranteed not to route to real hosts:
- Documentation ranges:
203.0.113.0/24(TEST-NET-3) or198.51.100.0/24(TEST-NET-2) - Private ranges:
172.16.0.0/12or specific unused addresses within your local network
This prevents accidental traffic leakage while ensuring the host can bind services to the chosen address.
Register the Local DNS Domain
Create the DNS mapping using the container CLI. This command writes a resolver file to /etc/resolver/ (on macOS) and registers the mapping with the container's internal DNS server:
sudo container system dns create <domain-name> --localhost <ipv4-address>
For example, to map host.container.internal to 203.0.113.113:
sudo container system dns create host.container.internal --localhost 203.0.113.113
The system validates the input and immediately makes the domain resolvable for all containers using the container runtime.
How the DNS Resolution Works
LocalhostDNSHandler Implementation
In Sources/APIServer/LocalhostDNSHandler.swift, the DNS server implements LocalhostDNSHandler to manage dynamic hostname resolution. This component uses DirectoryWatcher to monitor the resolver directory for changes and maintains an in-memory lookup table of active domains.
When DNS queries arrive, the handler consults its internal [DNSName: IPv4Address] dictionary to answer requests for locally configured domains.
Resolver File Parsing
The handler parses resolver files using the pattern defined in HostDNSResolver.localhostOptionsRegex. Each valid entry builds a mapping entry that associates the DNS name with the specified IPv4 address, enabling the server to return the correct A record when containers query for the custom domain.
Automatic Search Domain Configuration
As defined in Sources/ContainerPersistence/ContainerSystemConfig.swift, the DNSConfig.defaultDomain property automatically populates the search list in container resolvers. This means containers can resolve the custom domain using unqualified hostnames (e.g., host.container.internal resolves correctly without suffixes).
Complete End-to-End Example
The following workflow demonstrates running a Python HTTP server on the host and accessing it from a container using the custom DNS domain:
# 1. Choose a non-conflicting IP from the documentation range
IP=203.0.113.113
DOMAIN=host.container.internal
# 2. Register the domain with the container DNS service
sudo container system dns create ${DOMAIN} --localhost ${IP}
# 3. Start a simple HTTP server on the host (binding to the chosen IP)
mkdir -p /tmp/test && cd /tmp/test && echo "hello" > index.html
python3 -m http.server 8000 --bind ${IP} &
# 4. From a container, fetch the page using the DNS name
container run -it --rm alpine/curl curl http://${DOMAIN}:8000
# Output: hello
The container sends the DNS query to the host's embedded DNS server, which returns 203.0.113.113, allowing the curl request to reach the Python server.
Removing the DNS Configuration
To clean up the DNS entry when it is no longer needed:
sudo container system dns delete <domain-name>
This removes the resolver file from /etc/resolver/ and clears the entry from the LocalhostDNSHandler in-memory table, making the domain immediately unresolvable for new container lookups.
Summary
- Select a safe IP from documentation (
203.0.113.0/24) or private ranges (172.16.0.0/12) to avoid network conflicts - Use
container system dns createto register the domain and write resolver configuration to/etc/resolver - The
LocalhostDNSHandlerwatches resolver files and maintains a[DNSName: IPv4Address]dictionary for query responses - Containers automatically inherit the
DNSConfig.defaultDomainin their search list, enabling immediate resolution - Verify connectivity by running services on the mapped IP and accessing them from containers using the custom hostname
Frequently Asked Questions
What IP address ranges are safe to use for local DNS domains?
Use addresses from RFC 5737 documentation blocks such as 203.0.113.0/24 or 198.51.100.0/24, or private ranges like 172.16.0.0/12. These addresses are guaranteed not to conflict with public internet routing according to the apple/container source recommendations.
How do containers know to use the custom DNS domain?
The container runtime automatically appends the DNSConfig.defaultDomain (defined in Sources/ContainerPersistence/ContainerSystemConfig.swift) to each container's DNS search list. This allows unqualified hostname resolution without manual /etc/hosts modifications inside containers.
Where does the container DNS server store its configuration?
On macOS, the container system dns create command writes resolver files to /etc/resolver/. The LocalhostDNSHandler monitors this directory using DirectoryWatcher and updates its in-memory lookup table when files change, parsing entries that match HostDNSResolver.localhostOptionsRegex.
Can I use multiple custom domains simultaneously?
Yes. Run sudo container system dns create multiple times with different domain names and IP addresses. The LocalhostDNSHandler maintains a dictionary mapping each unique DNS name to its corresponding IPv4 address, supporting parallel resolution for multiple host services.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →