How Port Publishing Works with the `--publish` Flag in Apple Container

The --publish (or -p) flag creates a network bridge that maps a host address and port to a port inside the running container, forwarding TCP or UDP traffic through NAT rules configured by the daemon.

The Apple Container repository provides a container runtime that handles port forwarding through declarative CLI specifications. Understanding how the --publish flag translates user input into active socket bindings requires examining the parsing logic in the CLI client, the Container API service integration, and the underlying network configuration implementation.

Port Publish Syntax and Format

The --publish flag expects a publish specification string that defines the mapping between host and container network endpoints.

The Publish Specification Format

According to the project documentation in docs/how-to.md (lines 155-156), the flag accepts values in the form:


[host-ip:]host-port:container-port[/protocol]

This format consists of four distinct components:

  • host-ip (optional): The specific IP address on the host to bind. If omitted, the port binds to all host interfaces (0.0.0.0).
  • host-port: The TCP or UDP port on the host that will accept incoming traffic.
  • container-port: The destination port inside the container where the application is listening.
  • protocol (optional): Either tcp or udp (case-insensitive). Defaults to tcp if not specified.

The docs/command-reference.md (lines 65-67) lists this syntax as the standard interface for port exposure, allowing users to "forward TCP or UDP traffic from your loopback IP to the container you run."

Internal Implementation and Parsing

When you execute a container run command with --publish, the system processes this configuration through several architectural layers before establishing the network bridge.

From CLI Arguments to PublishPort Objects

In Sources/Services/ContainerAPIService/Client/Parser.swift, the CLI parser handles the --publish flag by creating PublishPort objects for each specification. Lines 586-605 contain the parsing logic that extracts the host IP, host port, container port, and protocol from the string, validating that ports fall within the 1-65535 range and that protocols are either tcp or udp.

These PublishPort objects store the parsed network configuration and are added to the container creation request sent to the daemon.

Network Bridge Configuration

The ContainerAPIService receives these configuration objects and instructs the networking subsystem to establish the port forwarding. According to Sources/Services/ContainerAPIService/Client/Utility.swift (lines 250-253), the service integrates the parsed publish data into the container configuration, specifying the socket bindings required for the runtime.

On the daemon side, this translates to creating NAT rules that map inbound traffic on host-ip:host-port to the container's network namespace at container-port. The protocol determines whether the system creates a TCP listener or a UDP socket.

Practical Usage Examples

The following examples demonstrate valid --publish syntax, as exercised in Tests/CLITests/Subcommands/Run/TestCLIRunCommand.swift (lines 715-938):


# Simple TCP publish: expose container port 8080 on host port 8080

container run -p 8080:8080 my-image

# Bind only to localhost (127.0.0.1)

container run -p 127.0.0.1:8080:8080 my-image

# UDP protocol forwarding

container run -p 6000:6000/udp my-image

# Port range mapping (maps host 7000-7010 to container 8000-8010)

container run -p 7000-7010:8000-8010 my-image

# Multiple publications on one container

container run -p 80:80 -p 127.0.0.1:443:443 -p 5000:5000/udp my-image

Validation and Edge Cases

The Apple Container runtime enforces specific constraints on port publishing to prevent configuration errors and security issues.

Port Range and Protocol Validation

The parser validates that all specified ports are integers between 1 and 65535. If the protocol is explicitly declared, it must be tcp or udp (case-insensitive). Invalid syntax results in an immediate error before the container creation process begins.

Privileged Ports and Binding Conflicts

When requesting host ports below 1024 (privileged ports), the container runtime must execute with elevated privileges. The test suite in Tests/CLITests/Subcommands/Run/TestCLIRunCommand.swift (lines 937-938) specifically validates this behavior by attempting to publish port 80 via 127.0.0.1:<privilegedPort>:80.

If the requested host port is already in use by another process, the daemon reports a binding error and the container fails to start. Similarly, specifying a host IP restricts connections to that specific interface, while omitting the IP binds to all available host interfaces (0.0.0.0).

Summary

  • The --publish flag uses the syntax [host-ip:]host-port:container-port[/protocol] to define network bridges between host and container.
  • Parser.swift (lines 586-605) converts these specifications into PublishPort objects with validated port ranges and protocols.
  • The ContainerAPIService processes these objects to create NAT rules and socket bindings, as implemented in Utility.swift (lines 250-253).
  • You can publish multiple ports, restrict bindings to specific IPs, use TCP or UDP protocols, and map port ranges using a single flag instance.
  • Privileged ports (below 1024) require elevated runtime privileges, and conflicts with existing host port bindings prevent container startup.

Frequently Asked Questions

Can I publish multiple ports when running a single container?

Yes. You can specify the --publish (or -p) flag multiple times in a single command to expose several ports simultaneously. Each flag instance creates a separate PublishPort object that the parser processes independently, allowing you to mix TCP and UDP protocols as well as different host IP bindings in one command.

What happens if I don't specify a host IP address in the publish specification?

If you omit the host-ip component from the publish specification, the container runtime binds the port to all available host interfaces (0.0.0.0). This allows connections from any network interface on the host machine. To restrict access to localhost only, explicitly specify 127.0.0.1 before the host port in the specification.

Does Apple Container support UDP port publishing?

Yes. While the default protocol is TCP, you can specify UDP by appending /udp to the publish specification. For example, container run -p 6000:6000/udp my-image creates a UDP listener. The parser in Parser.swift handles this protocol designation and configures the underlying socket as UDP rather than TCP.

Why does my container fail to start when attempting to publish port 80 or 443?

Ports below 1024 are privileged ports on Unix systems. Publishing these requires the container runtime to run with elevated privileges (root or appropriate capabilities). As tested in TestCLIRunCommand.swift, attempting to bind these ports without sufficient privileges results in a permission error during the socket binding phase in Utility.swift.

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 →