Skip to content

Podman - Ports and Volumes - Part 5

Difficult Level:
1
2
3
4
5

In this part we will talk about ports and volumes for podman. We will cover the basics on how to add them to your container and what to think about going forward.

Ports

Ports can be assigned both on container level and pod level and we will talk about both ways.

The purpose of port is simply to open up access into a container or pod from the outside to gain access to services within, may it be a web container, database container or containers with other services.

Port settings are defined by three parts.

  • Outside port
  • Inside port
  • Protocol

Think of it like this, we have a web server that should listen for incoming traffic on port 8080 and the inside port is 80, this will be with the protocol TCP (More on this later).

graph LR
 A(Client) --> |8080/tcp| B{Podman Network Stack<br>Internal translation}
 --> |80/tcp| C(Pod / Container) --> |80/tcp| B
 --> |8080/tcp| A

What this means is that podmans receives the request on port 8080 and the network stack translates the traffic from the outside port to the inside port and then to the service that runs on the server itself.

After that the server sends the data back to the network translation stack where it translates it from port 80 to port 8080 and finally back to the client.

There are never any direct traffic into the containers since running podman as rootless means you have one IP that podman listens to for all your pods/containers under normal operations.

To add port to a container you do that when you create the container either via podman run or podman create with the --publish parameter.

The port option can be individual ports like this.

$ podman create --name MyWebServer --publish=8080:80/tcp

Or a port range like this.

$ podman create --name MyWebServer --publish=8080-8085:80-85/tcp

One thing to take note of is that opening for both TCP and UDP requires a --publish for each protocol like this.

$ podman create --name MyWebServer --publish=8080:80/tcp --publish=8080:80/udp

When it comes to pods it is same as with containers with the --publish parameter and the options with port range and protocol.

$ podman pod create --publish=8080:80/tcp --name MyWebPod

Protocol

When setting up what to open up into your pods/containers you must chose on what protocol that it will listen on.

The two you can choose from is either TCP or UDP and these behave different from each other and for what purpose the serve.

I will only cover the basics so you get some understanding for when these are used.

TCP

TCP stands for Transmission Control Protocol and is the standard when it comes to communication that requires 100% accuracy of the data that is transferred.

The data that is sent is split up into small packets at the sender side with a added checksum bit and then transferred to the receiver, when the packet arrives to the receiver it checks the package against the checksum to see if the package is ok or not, if the package is not ok, that packet is sent again from the sender side to the receiving side to make the transfer complete.

It is a more robust failsafe protocol, but has a drawback that it is not as fast as UDP and not used for the same type of data.

UDP

UDP stands for User Datagram Protocol and is standard for data that do not require 100% accuracy and where there are no validity control on each and every packet that is sent.

UDP is mainly used for service that send a lot of data quick in one direction, for example streaming services like Youtube, Netflix, and so on, here the receiver do not report back on lost packets nor do the sender care if a packet here and there are lost if the receiver part still actively accept the flow of data that goes in one direction.

This makes the UDP protocol send data at a much faster rate than TCP.

Many game services utilize both TCP and UDP to maximize user experiences when gaming online so it is not one protocol to rule them all.

VLANs

Yes if running in rootful mode you can take advantage of VLAN and assigning each container/pod to a specific network & IP, but then you are running your pods at highest security level and this we wish not to do and in return have a more secure container environment.

So this we will not cover in this series, since we are focused on the rootless way of running things.

Volumes

Volumes are an integral part of data storage in containers, without them no data is persistent and will now survive container upgrades or container decommissions.

As standard all volumes are stored in .local/share/containers/storage/ in your container users home directory, but can be moved to other location if needed.

Volumes are added to a container when you either run the create or run parameter on podman, we will go through both of them even if their syntax is exactly the same.

We will use our web server we created earlier, so if you still has it running, run this command to remove it so we can recreate it with a volume attached.

$ podman rm -f MyWebServer

Now we are at square one and can continue.

Run the following.

$ podman run --name MyWebServer --publish=8080:80/tcp \
--volume vol-mywebserver:/usr/share/nginx/html:ro \
--detach docker.io/library/nginx

Now we created a web server with external storage and now we will break down the --volume parameter to explain some options.

The settings consist of three parameters.

  • Storage on disk physical disk where data will reside, default is .local/share/containers/storage/volumes.
  • Internal folder to store on disk, must be an absolut path inside the container.
  • The security mode on the attached volume.

So the first part we wrote vol-mywebserver and that makes podman create a volume named that and put it in .local/share/containers/storage/, the second part is what we want to map from inside the container to external storage, in this case /usr/share/nginx/html, this one must be the full path inside the container.

The last part indicates that it should only be mounted in Read-Only mode, default is rw which is Read-Write.

By just exchanging the run for create we can use almost the same syntax as above, excluding the --detach, this one is not used in create.

$ podman create --name MyWebServer --publish=8080:80/tcp \
--volume vol-mywebserver:/usr/share/nginx/html:ro \
docker.io/library/nginx

Since we already created our server with the run parameter we cannot create it again, but as you see, not much is different in both of the commands.

There are more option to the security mode and i recommend reading up on them, can be cases where default not always work, i know i use an option of Z as the security settings and this is what the manual says about it.

The Z option tells Podman to label the content with a private unshared label Only the current container can use a private volume.

More information from podman run manual about volume security

You can list your volumes with the following command.

$ podman volume list
DRIVER      VOLUME NAME
local       vol-mywebserver

The downside is that you do not see what container it is attached to if you do not give it a name that matches, here we did that so we see by the VOLUME NAME that it is the web servers volume, but we cannot see its mount point inside the container nor the path in our storage.

For that we need to inspect both the container and the volume.

For the container look for the Binds json with the following command.

$ podman container inspect MyWebServer

JSON output.

...
    "Binds": [
        "vol-mywebserver:/usr/share/nginx/html:ro,rprivate,nosuid,nodev,rbind"
    ],
...

For the volume use this command.

podman volume inspect vol-mywebserver

JSON output.

[
    {
        "Name": "vol-mywebserver",
        "Driver": "local",
        "Mountpoint": ".local/share/containers/storage/volumes/vol-mywebserver/_data",
        "CreatedAt": "2023-08-28T18:17:16.132346403Z",
        "Labels": {},
        "Scope": "local",
        "Options": {}
    }
]

Now you can match the volume with the Bind mount.

Shared disk

Now we share disk with the rest of the operating system and that is not always desired, since images, containers and other volume data can get really big quick when you start adding data.

Here comes the file storage.conf to the rescue, placing it under .config/containers/ in your podman users home directory makes it override default settings for podman.

It will only override the specific settings we put in and if it would not find it in this file it uses it's own defaults.

[storage]
driver = "overlay"
rootless_storage_path = "/mnt/containerdata"

Here i put in /mnt/containerdata as an example and it is mounted on an second drive, you can place it wherever you want as long as it is placed where you have enough storage.

Important is that your user account running your containers is the only one that has full permission on this folder.

In this guide we used podman as our user so i will use that as an example, we would give it user and group permission to the folder and make it only readable for this account.

$ chown -R podman:podman /mnt/containerdata
$ chmod -R 770 /mnt/containerdata

When planning your podman container system take this into account from the start, before you start adding images, containers and volumes.

If you are running multi user podman environment you could add a directory for each user on an external disk and assign right permission on each folder and then ask them to update their storage.conf with right path and still separate containers from operating system disk, or move the entire home holder to separate disk, recommended to do while you are installing your operating system before users are added.

Recap

In this part we talked about port and volumes for your containers and pods, these are some of the essential parts for your environment, we barely scratch the surface on which options there is but it is still a good ground to build upon.

In the next part we will talk about the difference between pods and containers.