In this blog, you will take a look at Podman Compose. With Podman Compose, you can use compose files according to the Compose Spec in combination with a Podman backend. Enjoy!

1. Introduction

A good starting point and a must read is this blog provided by RedHat. In short, Podman Compose is not directly maintained by the Podman team, neither is Docker Compose, of course. Podman Compose has a more limited feature set than Docker Compose and in general, it is advised to use Kubernetes yaml files for this purpose. See a previous blog how this can be used. However, the Podman team will fix issues in Podman when required for Podman Compose. It is also possible to use Docker Compose in combination with Podman, as is described in this post.

In this post, you will use Podman Compose to run some basic containers. Sources used in this blog are available at GitHub and the container image is available at DockerHub. The container image is build in a previous blog, you might want to check it out when you want to know more about Podman compared to Docker. The image contains a basic Spring Boot application with one REST endpoint which returns a hello message.

2. Prerequisites

Prerequisites needed for this blog are:

  • Basic Linux knowledge;
  • Basic container knowledge;
  • Basic Podman knowledge;
  • Basic Docker Compose knowledge.

3. Podman Compose With One Container

Let’s start with a basic single container defined in compose file docker-compose/1-docker-compose-one-service.yaml. One service is defined exposing port 8080 in order to be able to access the endpoint.

services:
  helloservice-1:
    image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
    ports:
    - 8080:8080

It is assumed that you have Podman installed already. Execute the following command from the root of the repository.

$ podman-compose -f docker-compose/1-docker-compose-one-service.yaml up -d
Error: unknown shorthand flag: 'f' in -f

As expected, this does not work. Podman Compose is not included or available when you have installed Podman. Docker Compose does not require an additional installation. Podman Compose is a community project and not maintained by the Podman team and that is why it is not part of the Podman installation.

Installation instructions for Podman Compoe can be found at the Podman Compose GitHub pages. The easiest way is to use pip, the Python package installer. This requires an installed Python version and pip.

$ pip3 install podman-compose

Run the Podman Compose command again.

$ podman-compose -f docker-compose/1-docker-compose-one-service.yaml up -d
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 3.4.4
** excluding:  set()
['podman', 'ps', '--filter', 'label=io.podman.compose.project=docker-compose', '-a', '--format', '{{ index .Labels "io.podman.compose.config-hash"}}']
['podman', 'network', 'exists', 'docker-compose_default']
podman run --name=docker-compose_helloservice-1_1 -d --label io.podman.compose.config-hash=b2928552303ab947ea3497ef5e1eff327c1c9672a8454f18f9dbee4578061370 --label io.podman.compose.project=docker-compose --label io.podman.compose.version=1.0.6 --label PODMAN_SYSTEMD_UNIT=podman-compose@docker-compose.service --label com.docker.compose.project=docker-compose --label com.docker.compose.project.working_dir=/home/.../mypodmanplanet/docker-compose --label com.docker.compose.project.config_files=docker-compose/1-docker-compose-one-service.yaml --label com.docker.compose.container-number=1 --label com.docker.compose.service=helloservice-1 --net docker-compose_default --network-alias helloservice-1 -p 8080:8080 docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
302dcf140babae07c416e8556e11fc13918bd1fe3c52b737f4ab091f3599291e
exit code: 0

Verify whether the container has started successfully.

$ podman ps
CONTAINER ID  IMAGE                                                      COMMAND     CREATED             STATUS                 PORTS                   NAMES
3104b5a4c418  docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT              About a minute ago  Up About a minute ago  0.0.0.0:8080->8080/tcp  docker-compose_helloservice-1_1

Verify whether the endpoint can be reached.

$ curl http://localhost:8080/hello
Hello Podman!

Shutdown the container.

$ podman-compose -f docker-compose/1-docker-compose-one-service.yaml down
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 3.4.4
** excluding:  set()
podman stop -t 10 docker-compose_helloservice-1_1
docker-compose_helloservice-1_1
exit code: 0
podman rm docker-compose_helloservice-1_1
3104b5a4c4189777b21c2658a3d3c4df91b3f804d5c9bced63532f0318e9e9df
exit code: 0

Running a basic service just worked.

4. Podman Compose With Two Containers

Let’s run two containers defined in compose file docker-compose/2-docker-compose-two-services.yaml. One service is available at port 8080 and the other one at port 8081.

services:
  helloservice-1:
    image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
    ports:
    - 8080:8080

  helloservice-2:
    image: docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
    ports:
      - 8081:8080

Run the compose file from the root of the repository.

$ podman-compose -f docker-compose/2-docker-compose-two-services.yaml up -d
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 3.4.4
** excluding:  set()
['podman', 'ps', '--filter', 'label=io.podman.compose.project=docker-compose', '-a', '--format', '{{ index .Labels "io.podman.compose.config-hash"}}']
['podman', 'network', 'exists', 'docker-compose_default']
podman run --name=docker-compose_helloservice-1_1 -d --label io.podman.compose.config-hash=d73c6cebbe901d7e4f27699b4308a39acaa4c3517293680c24ea1dce255177cf --label io.podman.compose.project=docker-compose --label io.podman.compose.version=1.0.6 --label PODMAN_SYSTEMD_UNIT=podman-compose@docker-compose.service --label com.docker.compose.project=docker-compose --label com.docker.compose.project.working_dir=/home/.../mypodmanplanet/docker-compose --label com.docker.compose.project.config_files=docker-compose/2-docker-compose-two-services.yaml --label com.docker.compose.container-number=1 --label com.docker.compose.service=helloservice-1 --net docker-compose_default --network-alias helloservice-1 -p 8080:8080 docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
ff344fb5e22800c2c7454d66572bbcad22a47536d0dc930960ea06844b7838f3
exit code: 0
['podman', 'network', 'exists', 'docker-compose_default']
podman run --name=docker-compose_helloservice-2_1 -d --label io.podman.compose.config-hash=d73c6cebbe901d7e4f27699b4308a39acaa4c3517293680c24ea1dce255177cf --label io.podman.compose.project=docker-compose --label io.podman.compose.version=1.0.6 --label PODMAN_SYSTEMD_UNIT=podman-compose@docker-compose.service --label com.docker.compose.project=docker-compose --label com.docker.compose.project.working_dir=/home/.../mypodmanplanet/docker-compose --label com.docker.compose.project.config_files=docker-compose/2-docker-compose-two-services.yaml --label com.docker.compose.container-number=1 --label com.docker.compose.service=helloservice-2 --net docker-compose_default --network-alias helloservice-2 -p 8081:8080 docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT
e88b6d4eb33b8869b2fa7c964cd153a2920558a27b7bc8cf0d2b9f4d881a89ee
exit code: 0

Verify whether both containers are running.

$ podman ps
CONTAINER ID  IMAGE                                                      COMMAND     CREATED         STATUS             PORTS                   NAMES
ff344fb5e228  docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT              35 seconds ago  Up 35 seconds ago  0.0.0.0:8080->8080/tcp  docker-compose_helloservice-1_1
e88b6d4eb33b  docker.io/mydeveloperplanet/mypodmanplanet:0.0.1-SNAPSHOT              33 seconds ago  Up 33 seconds ago  0.0.0.0:8081->8080/tcp  docker-compose_helloservice-2_1

Verify whether both endpoints are accessible.

$ curl http://localhost:8080/hello
Hello Podman!
$ curl http://localhost:8081/hello
Hello Podman!

In the output after executing the podman-compose command, you can see that a default network docker-compose_default is created. A network alias helloservice-1 is created for container 1 and a network alias helloservice-2 is created for container 2.

Enter container 1 and try to access the endpoint of container 2 using the network alias. Beware to use the internal port 8080 instead of the external port mapping to port 8081!

$ podman exec -it docker-compose_helloservice-1_1 sh
/opt/app $ wget http://helloservice-2:8080/hello
Connecting to helloservice-2:8080 (10.89.1.3:8080)
saving to 'hello'
hello                100% |*********************************************************************************************************************|    13  0:00:00 ETA
'hello' saved

As you can see, the services can access each other using the network alias.

Finally, stop the containers.

$ podman-compose -f docker-compose/2-docker-compose-two-services.yaml down
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 3.4.4
** excluding:  set()
podman stop -t 10 docker-compose_helloservice-2_1
docker-compose_helloservice-2_1
exit code: 0
podman stop -t 10 docker-compose_helloservice-1_1
docker-compose_helloservice-1_1
exit code: 0
podman rm docker-compose_helloservice-2_1
e88b6d4eb33b8869b2fa7c964cd153a2920558a27b7bc8cf0d2b9f4d881a89ee
exit code: 0
podman rm docker-compose_helloservice-1_1
ff344fb5e22800c2c7454d66572bbcad22a47536d0dc930960ea06844b7838f3
exit code: 0

5. Conclusion

Podman Compose works with some basic compose files. However, Podman Compose lacks features compared to Docker Compose. This list cannot be easily found (at least, I could not find it). This means that it is a risk of using Podman Compose instead of Docker Compose. Besides that, RedHat itself recommends using Kubernetes yaml files instead of compose files. If you are using Podman, it is better to use the Kubernetes yaml files for container orchestration.