Podman Pod Networking and Port Mapping

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
netavark-1.17.2-1.el10.x86_64
Applies to Any Linux host with Podman installed
Privilege Rootful examples on the lab host; rootless pod notes where behavior differs
Scope Pod-specific networking — shared namespace, IP inspection, localhost communication, port collisions, pod-level -p publishing, immutable mappings, user-defined networks, aliases, hostname, DNS options, static IP, multi-network pods, and pod-level --userns. Does not cover generic -p syntax depth, full Aardvark DNS, pod lifecycle, or Quadlet .pod units.
Related guides Podman networking modes

Pod networking changes the rules. Every member of a pod shares one network namespace — one IP, one localhost, and one port space. That is what makes app-plus-sidecar layouts work, and it is also what blocks you from publishing ports on individual members after the fact.

This guide assumes you already know what a pod is from Podman pods. Here we focus on what the shared namespace means for IPs, ports, DNS, and user namespaces.


How Podman pod networking works

text
Pod network namespace
         ├── one IP/interface set
         ├── one loopback interface
         └── one TCP/UDP port space
       ┌──────┴──────┐
       ▼             ▼
 Container A     Container B
 app:8080        sidecar

All containers that join a pod's network namespace share:

  • the same network interfaces
  • the same IP addresses
  • the same routing table
  • the same localhost
  • the same TCP and UDP port namespace

That is fundamentally different from two ordinary containers on a user-defined bridge, where each container gets its own IP and its own loopback.


Inspect the pod IP address

Create a pod and start it before adding members:

bash
podman pod create --name pod-network-demo

Podman prints the pod ID. Start the pod:

bash
podman pod start pod-network-demo

Add two member containers. Start with the first:

bash
podman run -d --pod pod-network-demo --name net-a registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Join a second member to the same pod:

bash
podman run -d --pod pod-network-demo --name net-b registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Inspect the first member's address:

bash
podman inspect net-a --format 'IP: {{.NetworkSettings.IPAddress}}'

Sample output:

output
IP: 10.88.2.166

Check the second member:

bash
podman inspect net-b --format 'IP: {{.NetworkSettings.IPAddress}}'

Sample output:

output
IP: 10.88.2.166

Both containers report the same address because they share the pod network namespace. The MAC address and network aliases in full inspect output also match across members.


Containers in a pod communicate over localhost

A service bound to 127.0.0.1:8080 inside one member is reachable from every other member at the same address.

Create a pod with a web server and a client container:

bash
podman pod create --name localhost-pod

Bring the empty pod to running state:

bash
podman pod start localhost-pod

Add the web server member:

bash
podman run -d --pod localhost-pod --name loc-web registry.access.redhat.com/ubi9/httpd-24

The UBI httpd-24 image listens on port 8080 inside the container namespace.

bash
podman run -d --pod localhost-pod --name loc-client registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

From the client member, request the service on shared localhost:

bash
podman exec loc-client curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/

Sample output:

output
403

HTTP 403 confirms the TCP connection succeeded — loc-client reached loc-web over shared localhost. A connection failure would print 000.

No container-name DNS is required for this pattern. Both processes share the same loopback interface.


Two pod containers cannot bind the same port

The pod namespace has one port space, like a single Linux host.

Start a pod and run the first httpd member:

bash
podman pod create --name collision-pod

Start the pod before adding members:

bash
podman pod start collision-pod

Run the first httpd member so it binds port 8080:

bash
podman run -d --pod collision-pod --name coll-a registry.access.redhat.com/ubi9/httpd-24

Try to start a second httpd in the same pod:

bash
podman run -d --pod collision-pod --name coll-b registry.access.redhat.com/ubi9/httpd-24

The second container exits. Read its logs:

bash
podman logs coll-b

Sample output (trimmed):

output
(98)Address already in use: AH00072: make_sock: could not bind to address [::]:8080
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:8080
no listening sockets available, shutting down

coll-a already owns port 8080 in the shared namespace, so coll-b cannot bind the same port.

On separate containers attached to a user-defined network, each could listen on container port 8080 independently because they have different network namespaces and IPs.


Publish ports when creating the pod

Host port mappings belong to the pod, not to individual members. Create the pod with -p:

bash
podman pod create --name web-pod -p 18080:8080

The mapping forwards host port 18080 to port 8080 in the pod namespace. The UBI httpd-24 image listens on 8080, not 80.

Start the pod and add the application:

bash
podman pod start web-pod

Attach the application container to the running pod:

bash
podman run -d --pod web-pod --name web-app registry.access.redhat.com/ubi9/httpd-24

Test from the host:

bash
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:18080/

Sample output:

output
403

The application only needs to listen on :8080 inside the shared namespace. Podman forwards host traffic through the pod-level mapping.

Inspect published ports on the infra container:

bash
podman port web-pod-infra

Sample output:

output
8080/tcp -> 0.0.0.0:18080

Use the infra container name (PODID-infra) with podman port when the pod name alone is not accepted. General -p syntax and protocol options live in Podman port mapping.


Do not publish ports on individual pod members

Creating a pod without port publication and then trying -p on a joining container fails.

With web-pod already running, attempt:

bash
podman run -d --pod web-pod -p 9090:8080 registry.access.redhat.com/ubi9/httpd-24

Sample output:

output
Error: invalid config provided: published or exposed ports must be defined when the pod is created: network cannot be configured when it is shared with a pod

A member container does not receive its own independent network namespace where new mappings can be added. Publish ports on podman pod create instead.


Pod port mappings cannot be added after creation

Podman does not support adding or changing port publications on an existing pod. If you create a pod without the -p flags you need, the fix is to recreate the pod with the correct mappings and reattach your containers.

text
pod create without -p
later need host port 18080
recreate pod with -p 18080:8080

Do not edit internal Netavark firewall rules to work around this — plan host ports at pod creation time.


Publish multiple pod ports

Pass multiple -p flags at pod creation:

bash
podman pod create --name multi-port-pod -p 18081:8080 -p 18443:443

Start the pod and add a member:

bash
podman pod start multi-port-pod

Add a member that listens on the mapped ports inside the pod:

bash
podman run -d --pod multi-port-pod --name mp-app registry.access.redhat.com/ubi9/httpd-24

List mappings through the infra container:

bash
podman port multi-port-pod-infra

Sample output:

output
8080/tcp -> 0.0.0.0:18081
443/tcp -> 0.0.0.0:18443

The 443 mapping is ready when a process inside the pod listens on that port. TCP/UDP and port-range syntax are covered in Podman port mapping.


Rootless pod port mapping

Rootless pods follow the same rule: publish on podman pod create -p. For rootless pods, private networking typically uses pasta. If the pod is attached to a rootless bridge network, published ports use rootlessport by default unless rootless_port_forwarder="pasta" is configured.

A rootless pod with -p 8080:80 can publish a high host port normally. Host ports below the unprivileged threshold (typically 1024) face the same restrictions as ordinary rootless containers. See Rootless Podman privileged ports for fixes — this article does not repeat those workarounds.


Pod network mode is chosen at pod creation

Attach a pod to a user-defined network at creation:

bash
podman network create pod-app-network --subnet 10.20.0.0/24

Create the pod on that network:

bash
podman pod create --network pod-app-network --name app-pod

Member containers inherit the pod's network attachment. They cannot independently choose --network frontend or --network backend after joining. Decide pod topology before you run podman run --pod.


Pod on a user-defined network

Inspect which network the pod uses:

bash
podman pod inspect app-pod --format '{{json .InfraConfig.Networks}}'

Sample output:

output
["pod-app-network"]

The pod — not each member individually — holds the network attachment. Every container you add with --pod app-pod sees the same interface and IP from that network.


Pod-level DNS and Aardvark

On a DNS-enabled Podman user-defined network, Aardvark resolves network-scoped names. The pod's network endpoint can carry aliases; all members share that endpoint's resolver configuration.

Inside the pod, tightly coupled workloads normally use localhost:PORT rather than resolving each member as a separate DNS name. Member containers do not each get unique network IPs on the shared namespace.

Full Aardvark behavior and standalone container DNS belong in Podman container DNS.


Configure a pod network alias

Create a network and a pod with an alias:

bash
podman network create alias-net --subnet 10.30.0.0/24

Create the pod with a network alias:

bash
podman pod create --network alias-net --network-alias webapp --name web-alias-pod

Podman documents --network-alias for pod-wide aliases on the attached network. For multi-network pods, use --network NET:alias=NAME when the alias should apply only to one network.

Start the pod, then run a separate container on the same network:

bash
podman pod start web-alias-pod

The client container sits on alias-net but outside the pod:

bash
podman run -d --network alias-net --name alias-client registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Resolve the alias from outside the pod:

bash
podman exec alias-client getent hosts webapp

Sample output:

output
10.30.0.2       webapp.dns.podman

The alias resolves to the pod's shared network IP (10.30.0.2), not to a per-member address.


Pod hostname

UTS namespace is shared by default, so set hostname at pod creation:

bash
podman pod create --hostname application --name hostname-pod

Start the pod and add two members:

bash
podman pod start hostname-pod

Add the first member:

bash
podman run -d --pod hostname-pod --name hn-a registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Add a second member to confirm UTS is shared:

bash
podman run -d --pod hostname-pod --name hn-b registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Both members report the same hostname:

bash
podman exec hn-a cat /etc/hostname

Sample output:

output
application

Check the second member reports the same hostname:

bash
podman exec hn-b cat /etc/hostname

Sample output:

output
application

Custom pod DNS servers

Pass resolver options at pod creation:

bash
podman pod create --dns 10.0.0.53 --name dns-pod

Verify the setting on the pod:

bash
podman pod inspect dns-pod --format '{{json .InfraConfig.DNSServer}}'

Sample output:

output
["10.0.0.53"]

Pod-level --dns, --dns-search, and --dns-option apply to members through the shared pod networking environment. Resolver semantics for standalone containers are covered in Podman container DNS.


Static pod IP address

Request a fixed address on a user-defined network when creating the pod:

bash
podman pod create --network pod-app-network --ip 10.20.0.50 --name static-pod

Start the pod and add a member:

bash
podman pod start static-pod

Add a member and confirm it inherits the static address:

bash
podman run -d --pod static-pod --name static-c registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Confirm the address on the member:

bash
podman inspect static-c --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}}{{"\n"}}{{end}}'

Sample output:

output
pod-app-network 10.20.0.50

Pick an address inside the network subnet you created (10.20.0.0/24 here). The standalone --ip option applies only when the pod joins one network — see the multi-network section for per-network static addresses.


Multiple networks on one pod

Podman 5.8.2 can attach a pod to more than one network at creation:

bash
podman network create frontend --subnet 10.40.0.0/24

Create the backend network on a separate subnet:

bash
podman network create backend --subnet 10.41.0.0/24

Attach the pod to both networks at creation:

bash
podman pod create --network frontend --network backend --name multi-net-pod

If you need static addresses on a multi-network pod, set ip= on each --network entry; the standalone --ip option is limited to single-network pods:

bash
podman pod create \
  --network frontend:ip=10.40.0.10 \
  --network backend:ip=10.41.0.10 \
  --name multi-net-pod

Every member shares all pod network interfaces. Add one member:

bash
podman run -d --pod multi-net-pod --name mn-c registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

List every interface the member inherited:

bash
podman inspect mn-c --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}}{{"\n"}}{{end}}'

Sample output:

output
backend 10.41.0.2
frontend 10.40.0.2

Both interfaces appear on the same container because the pod namespace includes every network the pod was created with. Individual members cannot pick a subset after they join.


User namespace is a pod-level decision

Per-container --userns is ignored when joining a pod with an infra container. Attempting to set it on a member fails:

bash
podman pod create --name userns-pod

Start the pod, then try to override user namespace on a member:

bash
podman pod start userns-pod

Podman rejects per-member --userns when an infra container owns the pod namespace:

bash
podman run -d --pod userns-pod --userns=keep-id --name userns-member registry.access.redhat.com/ubi9/ubi-minimal sleep 3600

Sample output:

output
Error: cannot set user namespace mode when joining pod with infra container: invalid argument

Configure user namespaces when creating the pod:

bash
podman pod create --userns=keep-id --name userns-pod2

That member starts without the error. Mapping details live in Podman user namespaces.


Why podman exec is not needed for pod networking

To reach another service in the same pod, connect to localhost:PORT. Use podman exec when you need a separate process or shell inside a member — not as a network hop to another container's service.

text
Inside pod:  curl http://127.0.0.1:8080

Not needed:  podman exec other-container curl ...

Pod vs separate containers on a user network

Topic Pod Separate containers on user network
IP address shared across members one IP per container
localhost shared loopback private per container
Port namespace one shared space same container port can repeat
App to sidecar localhost:PORT DNS name such as sidecar:PORT
Host port publish on podman pod create on each podman run
Network choice fixed at pod creation per container

Use a pod when localhost coupling and joint port space are intentional. Use separate networked containers when each service needs its own IP and DNS endpoint.


Common pod networking errors

Symptom Likely cause Fix
-p rejected on podman run --pod Ports must be set on the pod Add -p to podman pod create
Address already in use in pod logs Two members bind the same port Use different listen ports or run one listener
Host cannot reach service after pod create No -p at pod creation Recreate pod with correct -p mapping
Member name does not resolve inside pod Shared namespace — no per-member DNS IP Use localhost:PORT
--userns ignored or rejected on member Pod infra owns the namespace Set --userns on podman pod create
Wrong network subnet or no route Pod attached to wrong network podman pod inspect; recreate on correct network

References


Summary

Podman pod networking gives every member one shared namespace: one IP, one localhost, and one port space. On the lab host, two members of pod-network-demo both showed 10.88.2.166, and loc-client reached httpd on 127.0.0.1:8080 without container-name DNS.

That sharing has consequences. A second httpd in the same pod failed with Address already in use on port 8080. Host port mappings must be declared on podman pod createpodman run -p inside an existing pod is rejected, and mappings cannot be added later.

Network design is also pod-scoped: user-defined networks, aliases, static IPs, DNS servers, and --userns are set when the pod is created. Use localhost for member-to-member traffic; use network aliases when another container on the same user-defined network must resolve the pod as a single endpoint.

For pod creation and namespace defaults beyond networking, see Podman pods. For per-container publish syntax and rootless forwarding detail, see Podman port mapping.


Frequently Asked Questions

1. Do containers in a Podman pod share the same IP address?

Yes. All members share the pod network namespace, so they see the same interfaces, IP addresses, routing table, and localhost. Inspect any member or the pod infra container to confirm the shared address.

2. Can I publish ports with podman run -p inside an existing pod?

No. Port mappings belong to the pod network namespace and must be set on podman pod create with -p. A container joining a pod cannot add its own publish rules; Podman returns an error if you try.

3. Why do two pod containers get address already in use on the same port?

Pod members share one TCP and UDP port space, like processes on one host. If one container binds 0.0.0.0:8080, a second member cannot bind the same port. On separate user-defined-network containers each has its own namespace and can reuse container port 8080 independently.

4. Can I add port mappings to a pod after it is created?

No on current Podman. Port publishing is fixed at pod creation time. If you need a new host mapping, recreate the pod with the correct -p flags and reattach your containers.

5. How do pod containers talk to each other?

Use localhost and the service port, for example curl http://127.0.0.1:8080. Member container names are not separate network endpoints inside the shared namespace. podman exec is for running commands, not for network hops between members.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)