# How to Publish a Container Port to a Specific Host IP Address in apple/container

> Publish container ports to specific host IP addresses with the -p flag. Learn how to bind container ports to localhost or any IP using apple/container for precise network control.

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

---

**Use the `-p` flag with the syntax `[host-ip:]host-port:container-port[/protocol]` to bind a container port to a specific IP address on the host, such as `127.0.0.1:8080:80` to restrict traffic to localhost.**

The `apple/container` CLI allows you to expose services running inside containers to specific network interfaces on your host machine. By specifying a host IP address in the port mapping, you can control which network interfaces accept incoming traffic, improving security and enabling multi-interface deployments.

## Port Mapping Syntax Explained

The complete syntax for publishing a port to a specific host IP follows this pattern:

```

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

```

Each component serves a specific purpose in the network stack:

- **host-ip** – Optional IPv4 or IPv6 address assigned to a host interface. If omitted, the system defaults to the loopback address (`127.0.0.1` for IPv4 or `[::1]` for IPv6).
- **host-port** – The TCP or UDP port number on the host that will accept connections.
- **container-port** – The port number inside the container where the application is listening.
- **protocol** – Optional transport protocol, either `tcp` (default) or `udp`.

When you invoke the `container run` command with this flag, the CLI parses the string in [`Sources/Services/ContainerAPIService/Client/Parser.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Parser.swift) via the `Parser.publishPort(_:)` method (lines 606-661), validates the input, and instantiates a `PublishPort` object that captures the host address, ports, and protocol.

## CLI Usage Examples

### Bind to IPv4 Loopback

Restrict access to the local machine only by binding to `127.0.0.1`:

```bash
container run -d --rm -p 127.0.0.1:8080:8000 node:latest npx http-server -p 8000

```

This forwards traffic from host `127.0.0.1:8080` to port `8000` inside the container.

### Bind to IPv6 Address

For IPv6 interfaces, enclose the address in brackets:

```bash
container run -d --rm -p '[::1]:8080:8000' node:latest npx http-server -p 8000

```

The runtime creates a socket forwarder bound to `[::1]:8080` in [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift) (lines 876-907).

### Publish UDP Ports

Specify the protocol explicitly for UDP services:

```bash
container run -d --rm -p 192.168.1.50:5353:53/udp alpine/dnsmasq

```

This maps host `192.168.1.50:5353` to container port `53` using UDP, which the parser identifies via the trailing `/udp` suffix and stores in the `PublishPort.proto` field.

### Map Port Ranges

Publish consecutive ports by specifying ranges:

```bash
container run -d --rm -p 127.0.0.1:9000-9004:8000-8004 myapp:latest

```

The `Parser.publishPort` method in [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift) expands these ranges into individual `PublishPort` entries with a `count` field representing the range length.

## How Port Publishing Works Under the Hood

### Parsing the -p Flag

In [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift) (lines 299-311), the `-p/--publish` flag is defined to collect string arguments from the command line. These strings are passed to `Parser.publishPort(_:)` in [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift), which uses regular expressions to extract the host IP, host port, container port, and optional protocol. The parser validates that host ports do not overlap and throws an error if conflicting mappings are detected.

### Configuration Storage

The parsed `PublishPort` objects are appended to `ContainerConfiguration.publishedPorts`, defined in [`Sources/ContainerResource/Container/ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/ContainerConfiguration.swift) (lines 27-30). This configuration structure serializes the port mappings and passes them to the runtime service when the container starts.

### Runtime Socket Forwarding

When the container launches, `RuntimeService.startSocketForwarders` (implemented in [`Sources/Services/RuntimeLinux/Server/RuntimeService.swift`](https://github.com/apple/container/blob/main/Sources/Services/RuntimeLinux/Server/RuntimeService.swift), lines 876-907) iterates over each `PublishPort`. For each entry:

1. Creates a `SocketAddress` for the host using the specified IP and port
2. Creates a corresponding `SocketAddress` inside the VM using the container's internal IP (`containerIPAddress`)
3. Spawns a lightweight proxy that forwards traffic between the host socket and container socket
4. Tears down forwarders automatically when the container stops

## Programmatic Port Configuration in Swift

You can construct `PublishPort` instances directly in Swift code for programmatic container configuration:

```swift
import ContainerResource

let publish = PublishPort(
    hostAddress: .ipv4("192.168.1.50"),
    hostPort: 5353,
    containerPort: 53,
    proto: .udp,
    count: 1
)

var config = ContainerConfiguration()
config.publishedPorts.append(publish)

```

Validate that ports do not overlap before starting the container:

```swift
if config.publishedPorts.hasOverlaps() {
    throw ContainerizationError(
        .invalidArgument,
        message: "host ports for different publish port specs may not overlap"
    )
}

```

The `hasOverlaps()` method is defined in [`Sources/ContainerResource/Container/PublishPort.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Container/PublishPort.swift) (lines 54-100).

## Summary

- **Use the syntax** `[host-ip:]host-port:container-port[/protocol]` with the `-p` flag to bind to specific host interfaces.
- **Default behavior** binds to loopback addresses (`127.0.0.1` or `[::1]`) when no host IP is specified.
- **Implementation** spans [`Flags.swift`](https://github.com/apple/container/blob/main/Flags.swift) for CLI input, [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift) for validation, [`ContainerConfiguration.swift`](https://github.com/apple/container/blob/main/ContainerConfiguration.swift) for storage, and [`RuntimeService.swift`](https://github.com/apple/container/blob/main/RuntimeService.swift) for socket forwarding.
- **Protocol support** includes TCP (default) and UDP via the `/udp` suffix.
- **Range mapping** allows publishing consecutive ports using hyphenated syntax like `9000-9004:8000-8004`.

## Frequently Asked Questions

### What is the default host IP if I don't specify one?

If you omit the host IP in the port mapping (e.g., `-p 8080:80`), the `apple/container` runtime defaults to the loopback address. For IPv4, this is `127.0.0.1`; for IPv6, it is `[::1]`. This default is applied during the parsing phase in [`Parser.swift`](https://github.com/apple/container/blob/main/Parser.swift) when the optional host IP component is missing from the input string.

### Can I publish multiple ports to different host IP addresses?

Yes. You can specify multiple `-p` flags in a single `container run` command, each targeting a different host IP address. For example: `-p 127.0.0.1:3000:3000 -p 192.168.1.50:8080:80`. The `RuntimeService` creates independent socket forwarders for each IP/port combination, provided the host ports do not overlap.

### How does the runtime handle port conflicts?

The CLI validation layer in `Parser.publishPort(_:)` checks for overlapping host ports before the container starts. If two port mappings specify the same host IP and port (or overlapping ranges), the parser throws an error. Additionally, the `PublishPort` model provides a `hasOverlaps()` method that you can call programmatically to validate configurations before submitting them to the runtime.

### Is IPv6 supported for host IP binding?

Yes. IPv6 addresses are fully supported. When specifying an IPv6 address in the `-p` flag, you must enclose the address in brackets to distinguish the colons from the port separator, such as `[::1]:8080:80` or `[2001:db8::1]:443:443`. The parser correctly identifies these as IPv6 addresses and passes them to the runtime socket forwarder.