How to use docker-compose with Podman on Linux

Podman is a free and open source alternative to Docker, created by Red Hat. Although Docker is probably more widespread, to the point that it became a synonym for “containers”, from a technical point of view, Podman is superior: it was designed, right from the start, to be daemonless, it is able to run without the need for root privileges, and is better integrated in the Linux environment. One of the things which potentially prevented many to migrate from Docker to Podman, was the lack of a full-fledged Podman equivalent for docker-compose. By creating a layer of compatibility between Podman and Docker, it is now actually possible to user docker-compose as if it was a Podman native tool.

In this tutorial, we see how to install docker-compose on the most used Linux distributions, and how to use it with Podman instead of Docker.

In this tutorial you will learn:

  • How to install podman, podman-docker and docker-compose on the most used Linux distributions
  • How to configure Podman to work with docker-compose
  • How to use docker-compose with Podman
How to use docker-compose with Podman on Linux
How to use docker-compose with Podman on Linux
Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution-agnostic
Software podman, podman-docker, docker-compose
Other None
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Installing packages

The first thing we need to do, in order to use docker-compose with Podman, is to install the podman-docker package. This package provides a compatibility layer between Docker and Podman, emulating the Docker CLI, but executing Podman commands, under the hood.

To perform the installation on Fedora and other distributions of the Red Hat family, we can run:

$ sudo dnf install podman podman-docker

To install the package on Debian and Debian-based distributions, instead, we can run:

$ sudo apt install podman podman-docker

Installing docker-compose

Now we have to install docker-compose. Two versions of docker-compose currently exists: the first one, “v1”, is the original and now deprecated version written in Python, which receives only security fixes. The second one, “v2” is written in Go, is actively developed, and the one packaged as a Docker plugin in the Docker official repositories.

Installing docker-compose v1

Most Linux distributions include a “docker-compose” package which provides the Python version of the tool. To install it on Fedora, we run:

$ sudo dnf install docker-compose



On RHEL and clones the docker-compose package is not available out of the box, neither it can be installed (at least at the moment of writing) from the EPEL repository. On those distributions, we have to install docker-compose v1 as a Python package with pip, the python package manager.

Since we don’t want to run pip as root, we install the package in its own dedicated virtual environment, as a standard, unprivileged user, and then create a symbolic link to the executable (alternatively we can use a tool like pipx). In the example below, I assume the ~/.local/bin directory exists, and is in our PATH:

$ python3 -m venv virtualenv
$ virtualenv/bin/pip install docker-compose
$ ln -s "${PWD}/virtualenv/bin/docker-compose" ~/.local/bin/docker-compose

To install docker-compose v1 on Debian and Debian-based distributions, we can run:

$ sudo apt install docker-compose

Installing docker-compose v2

As we already said, the official supported and actively developed version of docker-compose is v2. A universal, cross-distribution way to install it, is by downloading the pre-compiled binary for our system architecture, straight from the project GitHub repository. In the example below, we download the Linux x86_64 version of the latest release (2.27 at the moment of writing):

$ curl -LO https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-x86_64

After we download the binary, we make it executable, then move it under a directory in our PATH. To install it just for our user, we can move it to ~/.local/bin, to install it system-wide, instead, we can move it to /usr/local/bin:

$ chmod +x docker-compose-linux-x86_64 
$ sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose

An alternative way to install docker-compose v2, is from the official Docker repository. First we add the repository to our distribution, following the official guide, then, we install the “docker-compose-plugin” package:

$ sudo dnf install docker-compose-plugin

When running the command above, we install docker-compose as a Docker plugin rather than a standalone binary: to use it as such, it is enough to create a symbolic link under a directory in our PATH:

$ sudo ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose

Enabling and starting the Podman socket

One of the main differences between Podman and Docker is their design. Docker uses a client-server architecture: the Docker daemon runs in the background, normally with root privileges (although recent versions of Docker support running a rootless, user-level version of the daemon), while Podman uses the so called “fork-exec” architecture (each container runs as a Podman child process). Since docker-compose is meant to work with Docker, it expects the docker daemon to be running. For this reason, Podman provides the “podman.socket” systemd unit. In order to use docker-compose with Podman with need to start and enable it:

$ sudo systemctl enable --now podman.socket



If we want to use docker-compose with a rootless Podman instance, we need to enable and start a user-level instance of the socket:

$ systemctl --user enable --now podman.socket

In this latter case, we also need to define and export the DOCKER_HOST environment variable. The traditional way to do this is by adding the line below to ~/.bash_profile or ~/.profile, depending on the shell we are using:

export DOCKER_HOST=unix:///run/user/1000/docker.sock

To make the changes immediately effective in our current shell instance, without waiting for the next login, we can source the file directly:

$ source ~/.bash_profile

Systemd socket vs. service

Beside the socket unit we activated in the previous step (/usr/lib/systemd/system/podman.socket), the Podman package comes with a systemd “service” unit (/usr/lib/systemd/system/podman.service); what does this service actually accomplishes, and why do we use the socket unit and not the service directly?

To see the command Systemd executes when it starts the Podman service, it is enough to take a look at the unit itself. What interests us, in this case, is the value of the “ExecStart” option, in the “Service” section:

ExecStart=/usr/bin/podman $LOGGING system service

We can see that when the service starts, Systemd executes the podman system service command. What this command does is creating a listening service that answers API calls for Podman. This way, although Podman doesn’t itself need a daemon to work, Podman can “imitate” the Docker interface.



Why we enable and start the socket unit, instead of the service directly? Doing so, we take advantage of a feature of Systemd: socket activated services. The socket listens for connections and starts the service on request, utilizing resources in the most efficient way.

Using docker-compose

Once everything is in place, we can use docker-compose as if we were running Docker under the hood. The one below is a basic example of a compose file. It spawns a container for a MariaDB server, and one for phpMyAdmin:

version: '3.7'
services:
  mariadb:
    image: docker.io/mariadb
    volumes:
      - db:/var/lib/mysql
    environment:
      TZ: "Europe/Rome"
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpassword
      MYSQL_DATABASE: testdb

  phpmyadmin:
    image: docker.io/phpmyadmin
    environment:
      PMA_HOST: mariadb
    ports:
      - "80:80"
    depends_on:
      - mariadb

volumes:
  db:

To create the containers, the volumes, and a dedicated network for the stack, we run:

$ sudo docker-compose up

Conclusions

In this tutorial we learned how to setup Podman to work with docker-compose. Docker-compose was designed to work with Docker, which, unlike Podman, uses a client-server architecture. By enabling the Podman socket and installing the podman-docker package, we basically create a layer of compatibility which let us transparently use docker-compose with Podman.



Comments and Discussions
Linux Forum