How container networking works on a Mac
macOS cannot run Linux containers directly, so every container app on the Mac runs them on a Linux side and relays traffic between it and the Mac. Three paths matter:
- Inbound: a published port on the Mac forwards to a container.
- Outbound: a container opens a connection to the internet, your company network, or a service on the Mac.
- Names: a container looks up a hostname.
When these paths bypass the Mac's own networking, VPN routes don't apply, internal hostnames don't resolve, and a firewall can't see which app is talking. Oxcart is built so that they don't.
Outbound: your VPN, DNS and firewall still apply
Oxcart redirects outbound TCP from containers and build steps, IPv4 and IPv6, to the Mac, which opens the connection with its own sockets. The Mac's routing table decides where it goes, including routes your VPN client installs. Oxcart changes no Mac routes, VPN settings or firewall preferences.
Address lookups (A and AAAA) use the macOS system resolver, including /etc/hosts and the resolver selection your VPN configures, so company hostnames resolve in containers the way they do in your browser. Container names stay scoped to their Docker networks, and DNS works over UDP and TCP.
Little Snitch, LuLu and endpoint agents
A network filter often holds the first connection from a new program for about two seconds while it asks you. Oxcart does not wait on that: connections take a fallback path until the main link is proven, a slow or failed connection moves to the fallback on its own, and three failures in a row take the link down for 30 seconds before it is probed again. Approve Oxcart once in your filter and its rules apply from then on.
Inbound: published ports
Publish ports with the normal Docker syntax:
# Mac loopback only (recommended for development)
docker run -d -p 127.0.0.1:8080:80 nginx
# Ephemeral host port, UDP
docker run -d -p 127.0.0.1::9000/udp my-udp-service
# IPv4 and IPv6 listeners on the Mac
docker run -d -p "[::]:8080:80" nginx A binding without an address listens on all of the Mac's IPv4 addresses, which makes it reachable from other machines on your network unless the macOS firewall blocks them. TCP and UDP can use the same host port number. Published listeners are released when the container stops.
Published ports also answer on the engine's own localhost, so a registry you run in a container works as a push target:
docker run -d -p 127.0.0.1:5000:5000 --name registry registry:2
docker tag my-app localhost:5000/my-app
docker push localhost:5000/my-app The Ports view: why a port isn't answering
Oxcart's Ports view lists every listener on your Mac and every container port, grouped by repository, worktree and branch, so two checkouts of the same project don't blur together. Each binding is ready, pending or error, with the reason:
- the address is already in use on the Mac;
- the server inside the container listens only on
127.0.0.1; - nothing is listening on that port yet.
Ports that other machines can reach carry a marker. Your coding agent sees the same information through Oxcart's MCP ports tool; see Docker for AI coding agents.
Reaching the Mac from a container
host.docker.internal resolves in containers and in build steps, and reaches services bound to the Mac's 127.0.0.1 over TCP. Point a container at a database or a dev server running natively on the Mac:
docker run --rm curlimages/curl http://host.docker.internal:3000/health Treat this as a deliberate opening: any container you run can reach services on your Mac's loopback this way, so don't leave unauthenticated admin services on 127.0.0.1 while running containers you don't trust.
The encrypted link between the Mac and the engine
Traffic between the Mac and the Linux side crosses a virtual network that macOS shares with other virtualization software on the same Mac. Oxcart carries published and outbound TCP on that link inside TLS 1.3 (TLS_AES_128_GCM_SHA256), pinned to a certificate made fresh at every engine start, with a 256-bit bearer token per start. An invalid token closes the connection, and the Linux side closes connections that don't come from the Mac's address before TLS begins.
The threat this addresses: other software on that shared network spoofing addresses to read or inject traffic, or to open new connections to your containers. A guest firewall also keeps that software away from unpublished container ports and the internal resolver. Encryption has a cost on that link, and it is a cost we chose. The Docker API itself never goes over the network: it is a private Unix socket, and with no ports published Oxcart holds no TCP or UDP socket on the Mac.
Current limits
- Host network mode (
--network host) and IPv6 container networking are not implemented; containers have IPv4 addresses. host.docker.internalcarries TCP; UDP to the host gateway is not implemented.- Published TCP and UDP don't preserve the client's address: the container sees the Linux side as the peer.
- Non-address DNS records (such as SRV and TXT) use the network's DHCP resolvers rather than macOS split DNS.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Connection refused on localhost:PORT | Server listens on 127.0.0.1 inside the container | Listen on 0.0.0.0; the Ports view names this case |
| "port is already allocated" | Another process or container holds the host port | Pick another host port; the Ports view shows the owner |
| Internal hostname doesn't resolve in a container | Name only exists on your VPN's DNS | Connect the VPN on the Mac; address lookups follow the Mac's resolver |
| Container can't reach a dev server on the Mac | Using localhost inside the container | Use host.docker.internal |
| First pull stalls for a couple of seconds | A network filter is asking about a new connection | Approve Oxcart once; connections use the fallback meanwhile |
| Teammates can reach your dev port | Published without an address | Publish to 127.0.0.1 |
Questions
Do Docker containers on a Mac use my VPN?
In Oxcart, yes. Outbound connections from containers are opened by the Mac itself, so the Mac's routes, VPN, split DNS for address lookups and per-app firewall apply to them. Oxcart changes no Mac routes, VPN settings or firewall preferences.
How do I reach a service on my Mac from a container?
Use host.docker.internal. In Oxcart it reaches services bound to the Mac's 127.0.0.1 over TCP, from containers and from build steps. --add-host mac:host-gateway gives you another name for the same address.
Why can I see the container running but not reach its port?
Usually the server inside the container listens on 127.0.0.1, which published ports cannot reach; make it listen on 0.0.0.0. The other causes are a host port already in use or nothing listening yet. Oxcart's Ports view shows which one it is.
Does Oxcart work with Little Snitch and LuLu?
Yes. When a network filter holds Oxcart's first connection while it asks you, connections take a fallback path until the link is proven, so nothing breaks or waits on your answer. A slow or failed connection moves to the fallback on its own.
Is container traffic encrypted on my Mac?
Published and outbound TCP between the Mac and Oxcart's engine travels inside TLS 1.3, pinned to a certificate made at every engine start, with a 256-bit token per start. It protects that link from other virtualization software on your Mac's shared network. Traffic to the internet is protected by whatever the app itself uses, such as HTTPS.
Are my published ports visible to other machines on my network?
A port published without an address listens on all of the Mac's IPv4 addresses, so other machines can reach it unless the macOS firewall blocks them. Publish to 127.0.0.1 to keep it on your Mac. Oxcart marks ports that other machines can reach.
Does Oxcart support host networking or IPv6 for containers?
No. Host network mode and IPv6 container networking are not implemented. Containers have IPv4 addresses; ports can still be published on IPv6 with "[::]:8080:80".
Sources and method
Networking behavior described here was tested on Oxcart on October 10, 2026, including outbound TCP, DNS, published ports, host access and TLS on the Mac-to-container link.