Skip to main content

Managed Service Podman

Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. Containers can be run on our managed servers in rootless mode.

Since the syntax is mostly identical to Docker, you can add the following alias for easier use:

alias docker=podman

Please keep in mind that the Podman syntax can change with newer versions and will no longer be identical to Docker eventually.

The full documentation of the Podman project can be found here:

https://podman.readthedocs.io/en/latest/index.html

Help and man pages are available in the command line:

podman --help
podman <subcommand> --help

man podman
man podman-<subcommand>

Cleanup Jobs

By default, we automatically create two cleanup jobs during the installation of Podman. One is running in the night from Sunday to Monday and will remove all unused Images. The second one is running every night to the 1st of each month and will remove all unused volumes.

These are safety measures to keep the footprint of Podman as minimal as possible and reduce the risk to overfill your disk space.

If you want to change these cleanup jobs, just write us a ticket and we will adapt it to your needs.

Searching, pulling & listing images

Search for images on remote registries with keywords:

podman search <search_term>

Enhance your search results with filters:

podman search ghost --filter=is-official

Pull the image that you would like to have locally:

podman pull docker.io/library/ghost

List all the images present on your environment:

podman images
note

Podman searches in different registries. It's recommended to use the full image name (e.g. docker.io/library/ghost instead of ghost) to ensure, that you are using the correct image.

Running a container

We run a sample Ghost container that serves the easy-to-use Ghost CMS.

podman run -dt -p 8080:2368/tcp docker.io/library/ghost
note

This container starts in detached mode -d. This means you will get a container ID after the container has been started. With the option -t, a pseudo-tty will be added to run arbitrary commands in an interactive shell.

With the -p 8080:2368/tcp option, we use port forwarding to be able to access the webserver of Ghost running on port 2368 through the TCP port 8080 on the host system.

Show running containers

podman ps -a gives us an overview of created and running containers.

$ podman ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5728ad900bc4 docker.io/library/ghost:latest node current/inde... 4 hours ago Up 4 hours ago 0.0.0.0:8080->2368/tcp gifted_edison

Attach to a running container

With the CONTAINER ID you are able to attach to an already running container. You can catch the ID in the podman ps output.

$ podman attach b3376ff455a0

[2020-06-10 09:17:15] INFO "GET /" 200 512ms

Test the running container

The container is now reachable on the port 8080 on your host system. As you may have noticed above in the Podman ps output, the container has no IP address assigned.

You can test with curl if your Ghost container application is running correctly:

$ curl localhost:8080| grep "og:site"

<meta property="og:site_name" content="Ghost" />

Proxy the port using nine-manage-vhosts

If you already have the managed services Nginx or Apache2 running, you can simply use nine-manage-vhosts to expose your application to the outside world using a Let's Encrypt enabled vhost.

Create the new vhost:

sudo nine-manage-vhosts virtual-host create testdomain.org --template=proxy --template-variable=PROXYPORT=8080

Register at let's encrypt:

sudo nine-manage-vhosts certificate register-client  --contact-email=contact@yourcompany.org

Create and enable a new let's encrypt cert on the vhost:

sudo nine-manage-vhosts certificate create --virtual-host=testdomain.org
sudo nine-manage-vhosts virtual-host update testdomain.org --template=proxy_letsencrypt_https --template-variable=PROXYPORT=8080

Only needed with apache webserver: To automatically redirect from http to https with using a Let's Encrypt certificate, you can set the template proxy_letsencrypt_https_redirect.

docker-compose

Starting with Ubuntu 22.04 Podman supports docker-compose natively and your installation includes docker-compose v1.

Install the latest v2 version of docker-compose
mkdir -p ~/bin
curl -L $(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r '.assets[] | select(.name == "docker-compose-linux-x86_64") | .browser_download_url') -o ~/bin/docker-compose
chmod +x ~/bin/docker-compose

If you are using Podman on Ubuntu 20.04, you can't use native docker-compose. However, you can use podman-compose, which replicates the behavior:

podman-compose
mkdir -p ~/bin
curl -o ~/bin/podman-compose https://raw.githubusercontent.com/containers/podman-compose/main/podman_compose.py
chmod +x ~/bin/podman-compose
ln -s ~/bin/podman-compose ~/bin/docker-compose # compatibility for the docker-compose command

Example Usage of docker-compose:

Next, we will run Ghost CMS in network mode slirp4netns with a compose file. slirp4netns is Podman's default network driver and can be omitted in the compose file. For this example, we use an already locally running MySQL database named nmd_ghost.

Prepare your own docker-compose.yaml File.

# by default, the Ghost image will use SQLite (and thus requires no separate database container)
# we have used MySQL here merely for demonstration purposes (especially environment-variable-based configuration)

services:
ghost:
image: ghost:3-alpine
restart: always
ports:
- 8080:2368
environment:
# see https://docs.ghost.org/docs/config#section-running-ghost-with-config-env-variables
database__client: mysql
database__connection__host: localhost
database__connection__user: nmd_ghost
database__connection__password: EeNae5xaoapoh5RoDah1muwu
database__connection__database: nmd_ghost
# this url value is just an example, and is likely wrong for your environment!
url: http://testdomain.org

The docker-compose.yaml file can then be run by the docker-compose command:

docker-compose -f docker-compose.yml up

Generate systemd user unit files

We recommend creating a systemd user service so that the container starts automatically after a system reboot.

Generate the systemd user unit files of the pod named examplepod:

podman generate systemd --new --files --name examplepod

HINT: with podman ps and podman pod ps, you can see the NAMES of your running pods, to generate the correct systemd unit files.

Copy the generated systemd user unit files into your systemd directory:

cp -pv /home/www-data/pod-examplepod.service /home/www-data/container-examplepod_ghost_1.service ~/.config/systemd/user/

Finally, enable the systemd user processes:

systemctl --user enable container-examplepod_ghost_1.service
systemctl --user enable pod-examplepod.service

Networking

Modes (Host, Bridged)

In Bridged (default) mode, all containers in the same Podman pod are sharing the same network namespace. Therefore, the containers will share the same IP, MAC address and port mappings.

Between the containers in one pod, you can always communicate using localhost.

There exists another mode called Host, which can be specified to podman using the network=host parameter.

If you use the Host network mode for a container, that container's network stack is not isolated from the Podman Host (the container shares the host's networking namespace), and the container does not get its own IP address allocated. With the Host mode, it's possible to connect to a local MySQL daemon running on a managed server or to connect to other TCP ports exposed on the host system.

Rootless

As we are running all our containers rootless, the network is set up automatically.

Port Publishing

Only so-called "high ports" can be published with rootless containers. All ports below 1024 are privileged and cannot be used for publishing.

Instead of publishing port 80, we need to switch to a higher port. In this example we will use our Ghost container, which is running on port 2368, and publish it on TCP port 8080 on localhost:

podman run -dt -p 8080:2368/tcp docker.io/library/ghost

You can use podman -P to automatically publish and map ports.

Check the published and occupied ports:

$ podman port -a

c0194f22266c 2368/tcp -> 0.0.0.0:8080

Container to Container communication

Communicating between two rootless containers can be achieved in multiple ways.

The easiest way is to use the published ports and the underlying host.

Check for listening containers:

podman ps

Show published ports and the own host IP:

podman port <container_id>
ip a

Run a new container to contact your host IP with the published port:

podman run -it --rm fedora curl <HOST_IP_ADDRESS>:8080

Volumes: Mount Storage to Container

To make data persistent, you'd either need to save it to an external system like a database or you mount local storage using the -v volumes flag. This will allow you to use two different mounting methods:

  • Bind Mounts
  • Named Volumes

Bind Mounts

Bind Mounts are created by mounting a file or directory inside the container.

Bind mounted volumes containging files and folders with subuids and subgids can be deleted with the following command:

podman unshare rm -r ${bind_mount_dir}

Named Volumes

Named Volumes are managed by Podman and can be changed with it's CLI. They are stored in a specific directory in ~/.local/share/containers/storage/volumes/.

Backup

All volumes data is automatically backed up on a managed servers. But a separate backup is probably necessary because of the following reasons:

  • You can't restore specific data out of volumes.
  • The data is not persisted in a consistent state (for example database storage).

That's why we'd recommend to create separate dumps of the data. Those dumps then get backed up automatically by our managed backup.