How Container Handles DNS Resolution Using HostDNSResolver

Container's HostDNSResolver manages local DNS resolution by creating, removing, and monitoring configuration files in /etc/resolver to route domain queries to localhost ports 1053 or 2053, then signals macOS's mDNSResponder daemon to reload its configuration.

Container, Apple's open-source containerization framework, delegates DNS resolution to the host macOS system through a lightweight resolver manager. The HostDNSResolver component bridges container networks with the host's DNS infrastructure by manipulating resolver files in the standard macOS resolver directory. This approach allows container-specific domains to resolve locally without modifying system-wide DNS settings.

HostDNSResolver Architecture and File Locations

The HostDNSResolver component operates as a thin abstraction over macOS's resolver directory. According to the source code in Sources/Services/ContainerAPIService/Client/HostDNSResolver.swift, the implementation targets FilePath("/etc/resolver") as the default configuration path (lines 24-26).

Each resolver file follows a strict naming convention: containerization.<domain>. This prefix ensures that Container-managed resolvers are easily distinguishable from system resolvers and can be safely manipulated without affecting other DNS configurations (lines 27-30).

Creating DNS Resolver Entries

The createDomain(name:localhost:) method generates resolver files that point DNS queries to specific localhost ports. When called with a localhost IP address, the resolver configures port 1053 and injects an options localhost:<ip> line into the file. Without a localhost specification, it defaults to port 2053 (lines 37-74).

The generated resolver file contains the nameserver 127.0.0.1 directive, ensuring that DNS queries for the specified domain are forwarded to the container-side DNS server running on the local machine.

Removing DNS Configuration

The deleteDomain(name:) method handles cleanup by reading the existing resolver file, extracting any embedded localhost IP address using regex pattern matching, and removing the file from /etc/resolver/ (lines 78-105).

This method returns the extracted IP address as an optional value, allowing callers to perform additional cleanup of associated resources. The regex extraction ensures that Container can track which localhost addresses were previously assigned to specific domains.

Listing Active Container Domains

To audit currently managed domains, the listDomains() method scans the /etc/resolver directory and filters files matching the containerization. prefix. It parses each file for the domain line and returns a sorted set of DNSName objects (lines 107-122).

This alphabetical sorting ensures deterministic output for testing and debugging purposes, as implemented in the source code.

Refreshing the macOS DNS Daemon

After creating or deleting resolver files, the system must notify macOS to reload its DNS configuration. The reinitialize() class method sends a SIGHUP signal to mDNSResponder using the killall -HUP mDNSResponder command (lines 124-141).

This signal forces macOS to re-read the resolver files in /etc/resolver, making DNS changes immediately effective without requiring a system restart.

Practical Implementation Example

Below is a complete usage pattern demonstrating domain creation, listing, and deletion using the HostDNSResolver API:

import Foundation
import ContainerizationExtras

// Initialize the resolver with default /etc/resolver path
let resolver = HostDNSResolver()

// Create a domain pointing to localhost port 1053
let domain = DNSName("myapp.local.")
let localhostIP = try IPAddress("127.0.0.1")
try resolver.createDomain(name: domain, localhost: localhostIP)

// Refresh DNS daemon to recognize changes
try HostDNSResolver.reinitialize()

// List all container-managed domains
let domains = resolver.listDomains()
print("Active domains: \(domains.map { $0.pqdn })")

// Delete domain and retrieve associated IP
if let previousIP = try resolver.deleteDomain(name: domain) {
    print("Cleaned up localhost IP: \(previousIP)")
}
try HostDNSResolver.reinitialize()

Summary

  • HostDNSResolver manages macOS DNS resolution by reading and writing files in /etc/resolver.
  • Files are prefixed with containerization. to avoid conflicts with system resolvers.
  • Port 1053 is used when a localhost IP is specified; otherwise, the resolver defaults to port 2053.
  • The deleteDomain(name:) method extracts localhost IPs using regex before removing files.
  • DNS changes take effect after calling reinitialize(), which signals mDNSResponder with SIGHUP.

Frequently Asked Questions

What file path does HostDNSResolver use for DNS configuration?

The resolver writes configuration files to /etc/resolver, which is the standard macOS directory for domain-specific resolver configurations. Each file is prefixed with containerization. followed by the fully-qualified domain name.

How does Container refresh DNS changes on macOS?

After creating or deleting resolver files, the reinitialize() method sends a SIGHUP signal to the mDNSResponder process using killall -HUP. This forces macOS to reload resolver configurations without requiring a system restart.

What is the difference between port 1053 and 2053 in HostDNSResolver?

When creating a domain with the localhost parameter specified, HostDNSResolver configures the resolver to use port 1053 and adds an options localhost:<ip> line. If no localhost IP is provided, the resolver defaults to port 2053 without the options line.

How does deleteDomain extract the localhost IP address?

The deleteDomain(name:) method reads the existing resolver file and applies a regex pattern to extract the localhost IP address embedded in the options line. It returns this IP as an optional value, allowing callers to clean up associated network resources after domain removal.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →