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

> Learn how the --publish flag in Apple Container maps host ports to container ports, forwarding TCP/UDP traffic efficiently. Discover seamless port publishing.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-06-20

---

**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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/Tests/CLITests/Subcommands/Run/TestCLIRunCommand.swift) (lines 715-938):

```bash

# 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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/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`](https://github.com/apple/container/blob/main/TestCLIRunCommand.swift), attempting to bind these ports without sufficient privileges results in a permission error during the socket binding phase in [`Utility.swift`](https://github.com/apple/container/blob/main/Utility.swift).