1. Overview

In this tutorial, we’ll look at the methods for enabling network communication between pods in a rootless Podman environment.

2. Rootless Podman v.s. Rootful Podman

Pod manager, commonly known as Podman, is an open-source container management tool for managing containers and pods. Contrary to the popular container engine Docker, Podman uses a daemon-less architecture to manage containers.

Podman offers two different modes: the rootful mode and the rootless mode. The two modes differ in the privileges the containers are created with. Specifically, the rootful mode creates a container with root privileges. On the other hand, the rootless mode creates a container with restricted permissions.

One great benefit of the rootless mode is that it offers better protection against security breaches in any container. Concretely, gaining access to a rootless container would severely restrict the damage an attacker can do to the system due to the lack of privileges. However, one downside of the rootless mode Podman is that communication between pods requires manual setup to enable.

3. Cross Pods Communication in Rootless Podman

In rootless Podman, communication between pods can be challenging due to network isolation. Unlike rootful Podman, which operates with elevated privileges, rootless Podman runs with limited permissions. The limited permissions restrict Podman from configuring low-level networks essential for cross-pod communication.

3.1. Pods Attach to a Default Network in Rootful Podman

In rootful Podman, new pods get automatically added to a default network — the podman network — if no network option is specified during creation. This means that in rootful Podman, cross-pod network connectivity is enabled by default as they’re living in the same network.

We can verify this behavior by creating a pod using the podman pod create command:

$ sudo podman pod create --name server-root
d23d7edf167e0cf3393845e3ba1debd68d5ba4be6d92f1e313a9f61f6d0f47bd

Importantly, we prefix the command with the sudo command to execute the creation as rootful Podman.

When we inspect the pod using podman inspect, we’ll see that the pod is attached to the podman network:

$ sudo podman inspect server-root | jq .[0].InfraConfig
{
  "PortBindings": {},
  "HostNetwork": false,
  "StaticIP": "",
  ...
  "Networks": [
    "podman"
  ],
  ...
}

In the command above, we again prefix the podman command with sudo to execute Podman commands in rootful mode. We pipe the output to the jq command to extract the InfraConfig key. Within the InfraConfig key, the Networks field tells us which networks the pod is currently attached to.

The output shows that our pod, server-root, is currently attached to the podman network.

3.2. Rootless Podman Do Not Attach Pods to Network

On the other hand, creating a pod in rootless Podman will not attach the pod to a network by default. Let’s create a pod using rootless Podman and check the network configuration:

$ podman pod create pod --name server-rootless
edf20040eab55935d521af3b72aedd062a066a12f3f63561d49f8f338dfbce56
$ podman inspect server-rootless | jq .[0].InfraConfig
{
  "PortBindings": {},
  "HostNetwork": false,
  "StaticIP": "",
...
  "Networks": null,
  "NetworkOptions": null,
...
}

As we can see, creating pods using rootless Podman leaves the network field empty, indicating that the pod is not attaching to any network.

There are generally two options we can take to enable communication between pods. The first option involves creating a custom network and putting all the pods into the network. The alternative is to publish the pod’s port to the host network, and have all the pods connect using the host network.

4. Sharing Network Namespace

We can put the different pods into the same custom network to facilitate communication between containers from different pods. Specifically, we can create a custom network using the podman network create command. Then, we attach the pod to the network during creation time using the –network option on the podman pod create command.

Let’s walk through an example to show how to set up a common network for pods.

4.1. Creating a Custom Network

Firstly, we’ll need to create a custom network that’ll be joined by both of the pods. To create a network, we can use the podman network create command:

$ podman network create shared-network
shared-network

The command creates a Linux bridge that acts like a virtual network switch. Any containers connecting to the network can communicate with each other using IP addresses as if they are on the same local network.

4.2. Creating Pods and Attaching to a Custom Network

Let’s create two pods, namely the server and client, that connect to the same network in our environment.

To create a pod and connect it to a network, we can use the podman pod create command with the –network option to create a network called shared-network:

$ podman pod create --name server --network shared-network
d8da1692ba3802a0e5d11b00a19f00cb19c566ba7a9bd9cb9c62ad16f1398736
$ podman pod create --name client --network shared-network
5a1a05d10d55ccfb300c78b60823aeafe58524d23b6e0408b4986b3636377cbc

Then, we verify the presence of the pods using the podman ps command:

$ podman pod ps
POD ID        NAME        STATUS      CREATED             INFRA ID      # OF CONTAINERS
5a1a05d10d55  client      Created     51 seconds ago      e5ab3f724da8  1
d8da1692ba38  server      Created     About a minute ago  b647f1ef5245  1

Before we proceed, let’s verify that the pods have successfully attached to the shared-network network:

$ podman pod inspect server | jq .InfraConfig.Networks
[
  "shared-network"
]
$ podman pod inspect client | jq .InfraConfig.Networks
[
  "shared-network"
]

As we can see, both pods are connected to the shared-network network.

Next, we’ll deploy a container to the server pod that serves HTML content in response to HTTP GET requests:

$ podman run --pod server -d --name http-server docker.io/crccheck/hello-world
15fb4409ae5cc0b62247a8488ecef1a97fcba4a60433b46da9c6b589ac99a941

The crccheck/hello-world image serves simple HTTP content on port 8000. This image is convenient for testing out network connectivity using a client-server setup.

To finish up, we create the client container that serves as the network packet sender pod:

$ podman run --pod client -dt --name http-client docker.io/jonlabelle/network-tools
c8a24d707bf273004548030131b96db1a45287086ba2e7fdf87fc2393cadaaf2

For the client, we use the jonlabelle/network-tools image. This image contains various network client command-line tools, including the curl command.

4.3. Testing the Connection From Client Pod to Server Pod

We can verify the connectivity between the server and client pod by issuing a network request. Specifically, we send an HTTP request from the http-client container to the server pod’s port 8000:

$ podman exec http-client curl -s http://server:8000
<pre>
Hello World


                                       ##         .
                                 ## ## ##        ==
                              ## ## ## ## ##    ===
                           /""""""""""""""""\___/ ===
                      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
                           \______ o          _,/
                            \      \       _,'
                             `'--.._\..--''
</pre>

As we can see, the curl request successfully gets the HTML response from the server pod. This confirms that we’ve successfully enabled the network communication between containers living in different pods.

5. Host Port Publication

The host port publication method requires pods that are expecting incoming traffic to publish on host ports. Then, the other pods can access the server pod by connecting to the host at the bounded port.

Extending from our example, the server pod needs to publish its port 8000 to the host port. Then, the client pod will connect to the server pod through the host network.

To illustrate the method, let’s create another pod, server-hostport. Notably, we publish the port 8000 using the –publish option:

$ podman pod create --name server-hostport --publish 8000:8000
36bfee4e199da2a25392e2db56f0a326640336101adb95638a7a7611804425e1

We can inspect the server-hostport pod to verify the port binding:

$ podman pod inspect server-hostport | jq .InfraConfig.PortBindings
{
  "8000/tcp": [
    {
      "HostIp": "",
      "HostPort": "8000"
    }
  ]
}

Similarly, we run the crccheck/hello-world container image on the server-hostport pod to serve the HTML content:

$ podman run -d --pod server-hostport --name http-server-hostport docker.io/crccheck/hello-world
665f7a9a4d839274be4866104763dd57709f657e31c9abd3dbcdb945361e468c

At this point, the host’s port 8000 is bound to the http-server-hostport container due to the pod’s port binding configuration. We can then connect to the server pod from the client pod by sending our request to the host network at port 8000. Importantly, we use the host.containers.internal hostname to access the host from within the container:

$ podman exec http-client curl -s http://host.containers.internal:8000
<pre>
Hello World
...
</pre>

6. Conclusion

In this tutorial, we’ve first learned that Podman is a software for managing containers and pods. Additionally, we’ve learned that we can run Podman in rootful or rootless mode where the latter is preferred due to stricter isolation and better security. We observed how network communication between pods in a rootless Podman environment is complicated as they do not share the same network.

Then, we learned that we can allow the network communication between the pods either attaching to a common custom network or publishing the port to the host’s network. We wrapped up by walking through both methods using actual examples.