How to Handle Networking Between Multiple Containers in Apple Container
The Apple Container tool leverages the macOS vmnet framework to create virtual networks where containers communicate via DNS hostnames, supporting isolated user-defined networks and automatic IP allocation between multiple containers.
The apple/container repository provides a Linux container runtime that creates lightweight virtual machines attaching to virtual networks managed by the macOS vmnet framework. Understanding how to handle networking between multiple containers requires knowledge of the three-tier architecture that manages network lifecycle, IP allocation, and DNS resolution. This guide explains the core components, CLI workflows, and programmatic APIs used to establish communication between containers.
Core Networking Architecture
The networking stack relies on three integrated components that bridge the vmnet framework with container orchestration.
container-network-vmnet XPC helper – This system service drives the vmnet framework, allocates IP addresses to containers, and exposes a client API through ContainerNetworkClient.
NetworksService – Implemented as a Swift actor in [Sources/Services/ContainerAPIService/Server/Networks/NetworksService.swift](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Server/Networks/NetworksService.swift), this component stores network configurations, registers the XPC service, and tracks the lifecycle of each network from creation to deletion.
ContainerDNSHandler – Located in [Sources/APIServer/ContainerDNSHandler.swift](https://github.com/apple/container/blob/main/Sources/APIServer/ContainerDNSHandler.swift), this handler resolves container hostnames by querying the IP allocations stored in NetworksService, enabling containers to reach each other by name rather than IP address.
Creating and Managing Networks
Networks in Apple Container fall into two categories: the automatic default network and user-defined isolated networks.
Default Network Behavior
When the system service starts, it automatically creates a default network named default. Every container joins this network unless explicitly configured otherwise. Containers on the default network share a common subnet and can communicate freely, but they remain isolated from containers on other user-defined networks.
User-Defined Networks
Create isolated networks using the CLI to segment container traffic:
container network create foo --subnet 192.168.100.0/24 --subnet-v6 fd00:1234::/64
Each user-defined network operates in complete isolation. Containers attached to network foo cannot communicate with containers on network bar or the default network unless a container is explicitly attached to multiple networks.
Internal Networks
Add the --internal flag during network creation to establish a host-only network that blocks all external traffic:
container network create isolated-backend --internal
Custom MAC and MTU Configuration
When attaching containers to networks, specify deterministic hardware addresses or adjust packet sizes using comma-separated options in the --network flag:
container run -d --network foo,mac=02:42:ac:11:00:02,mtu=1500 nginx
Connecting Containers to Networks
The runtime attaches virtual network interfaces to containers during the start phase and registers hostnames for DNS resolution.
Basic Container Attachment
Use the --network flag with container run to specify which network (or networks) a container joins:
container run -d --name web --network foo --rm nginx:latest
container run -d --name db --network foo --rm postgres:latest
Both containers share the foo network and can address each other via their hostnames (web and db) through the ContainerDNSHandler.
Multiple Network Attachment
Attach a container to several networks simultaneously to create gateway or bridge scenarios:
container run -d --name gateway \
--network default \
--network foo,mac=02:42:ac:11:00:02 \
--rm some-gateway-image
The first network listed determines which IP address receives published port forwards. The runtime configures each interface separately while maintaining the DNS entries for all attached networks.
DNS Resolution Mechanism
When a container queries a hostname, the ContainerDNSHandler performs A and AAAA lookups by querying NetworksService. If a hostname exists but lacks an IPv6 address, the handler returns a "NODATA" response rather than NXDOMAIN to accommodate musl libc's resolver behavior.
Practical Workflow for Multi-Container Networking
Follow this sequence to establish communication between multiple containers across isolated networks.
-
Create a dedicated network (optional but recommended for isolation):
container network create backend --subnet 10.0.0.0/24 -
Launch containers on the network:
container run -d --name api --network backend --rm myapp:latest container run -d --name cache --network backend --rm redis:latest -
Verify IP assignments and connectivity:
container network inspect backend -
Clean up unused networks when containers stop:
container network prune
The prune command removes any network not attached to a running container while preserving the default and system-managed networks.
Programmatic Network Management
Interact with the networking layer directly through Swift APIs for custom tooling or testing.
Creating Networks via NetworksService
import ContainerServices
let config = NetworkConfiguration(
name: "production",
mode: .bridge,
ipv4Subnet: "192.168.100.0/24",
ipv6Subnet: "fd00:1234::/64",
labels: ["env": "prod"],
plugin: "container-network-vmnet",
options: [:]
)
let network = try await networksService.create(configuration: config)
// The returned network contains the persisted configuration and runtime status
Resolving Container DNS
import ContainerAPI
let dnsHandler = ContainerDNSHandler(networkService: networksService)
let query = Message(questions: [Question(name: "api.production.test.", type: .A)])
if let answer = try await dnsHandler.answer(query: query) {
// answer contains the A record with the allocated IP address
}
Network configurations persist to disk via FilesystemEntityStore<NetworkConfiguration>, ensuring that subnet allocations and labels survive service restarts.
Summary
- Apple Container uses the macOS vmnet framework to provide virtual networking for Linux containers running as lightweight VMs.
- The default network automatically connects all containers unless overridden, while user-defined networks provide isolation between container groups.
- NetworksService manages network lifecycle and IP allocation, while ContainerDNSHandler enables hostname-based communication between containers.
- Attach containers to multiple networks using repeated
--networkflags, with the first network determining the primary IP for port forwarding. - Networks persist to disk and can be pruned when no longer in use, while internal networks provide host-only isolation for sensitive workloads.
Frequently Asked Questions
How do containers resolve each other's hostnames in Apple Container?
The ContainerDNSHandler queries the NetworksService to translate container names into IP addresses. When a container performs a DNS lookup for another container's hostname, the handler returns A or AAAA records based on the IP allocations stored in the network service. This allows containers to communicate using names like web or db instead of memorizing IP addresses.
Can a container belong to multiple networks simultaneously?
Yes. Specify multiple --network flags when running a container. Each flag can include optional mac and mtu parameters. The container receives a virtual network interface for each network, and the first network listed determines which IP address receives published port traffic. This configuration is useful for creating gateway containers that bridge isolated networks.
What is the difference between the default network and user-defined networks?
The default network is created automatically when the system service starts and connects all containers unless specified otherwise. User-defined networks are isolated from each other and from the default network, providing segmentation for multi-tier applications. You can create user-defined networks with custom subnets, IPv6 support, and internal (host-only) restrictions using the container network create command.
How does Apple Container handle network persistence and cleanup?
Network configurations are stored on disk via FilesystemEntityStore<NetworkConfiguration>, allowing subnet settings and allocations to survive service restarts. When containers stop, you can remove unused networks with container network prune, which deletes any network not attached to a running container while preserving the default and system networks.
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 →