Using Podman and Docker Compose
![Container ships docked](/sysadmin/sites/default/files/styles/full/public/2020-12/3-container-ships.jpg?itok=6rZacJ-E)
Image by Hessel Visser from Pixabay
Podman exists to offer a daemonless container engine for managing OCI-compliant containers on your Linux system. Users love it for its ease of adoption as an alternative to Docker. However, many users and the broader container community have been telling us that one missing feature is a "deal-breaker" for them. Up to now, support for Docker Compose, the command-line utility that orchestrates multiple Docker containers for local development, was missing. With Podman 3.0 now in development upstream, we have begun to support Compose. Here's how it works as a rootful/privileged user.
The following article discusses how to use Compose by using two examples that Docker has curated and maintained in the awesome-compose Git repository.
Start the Podman system service
I am currently using Fedora 33. Before running Compose, ensure that all the required packages are installed and set up the Podman (3.0 or greater) system service using systemd
. Other than Podman and its dependencies, be sure the podman-docker
and docker-compose
packages are installed. After installing the packages, start the Podman systemd
socket-activated service using the following command:
$ sudo systemctl start podman.socket
Verify the system service is running by hitting the ping endpoint and see if we get a response. This step needs to be successful before we can proceed further.
$ sudo curl -H "Content-Type: application/json" --unix-socket /var/run/docker.sock http://localhost/_ping
OK
We can now confidently run Compose knowing the RESTful API is working.
Examples
As mentioned earlier, I will demonstrate how to use Docker Compose with Podman through two examples. These examples are found at https://github.com/docker/awesome-compose. For these examples, I am cd'ing into the specific directories and executing commands.
Gitea with Postgres
This first example defines a base setup for the project Gitea, which describes itself as a community-managed lightweight code hosting solution written in Golang.
From within the awesome-compose
Git repository, cd
into gitea-postgres
and then issue the docker-compose up
command.
$ sudo docker-compose up
Creating network "gitea-postgres_default" with the default driver
Creating volume "gitea-postgres_db_data" with default driver
Creating volume "gitea-postgres_git_data" with default driver
Pulling gitea (gitea/gitea:latest)...
bf997e39f92f002e5fc91f6d7a2164ea8964a0a2028b680920d3ef4caa952b76: pulling image () from docker.io/gitea/gitea:latest
Pulling db (postgres:alpine)...
770f846caccea499131403039925f7b71078985f92c4cdc5706604fe87dbc665: pulling image () from docker.io/library/postgres:alpine
Creating gitea-postgres_db_1 ... done
Creating gitea-postgres_gitea_1 ... done
Attaching to gitea-postgres_db_1, gitea-postgres_gitea_1
db_1 | WARNING: no logs are available with the 'k8s-file' log driver
gitea_1 | WARNING: no logs are available with the 'k8s-file' log driver
The README for this docker-compose
setup says to visit localhost:3000 in your browser to verify it is working.
The Gitea instance is definitely working. By the Compose output, you can see that docker-compose
has created a network, two volumes, and two containers. We can observe the two containers in another terminal with the podman ps
command.
$ sudo podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
23c7bd43ae44 docker.io/library/postgres:alpine postgres About a minute ago Up About a minute ago gitea-postgres_db_1
7d8b7de5533b docker.io/gitea/gitea:latest /bin/s6-svscan /e... About a minute ago Up About a minute ago 0.0.0.0:3000->3000/tcp gitea-postgres_gitea
The network can be seen with podman network ls
.
$ sudo podman network ls
NAME VERSION PLUGINS
podman 0.4.0 bridge,portmap,firewall,tuning
gitea-postgres_default 0.4.0 bridge,portmap,firewall,tuning,dnsname
Lastly, the volumes can be displayed with podman volume ls
.
$ sudo podman volume ls
DRIVER VOLUME NAME
local gitea-postgres_db_data
local gitea-postgres_git_data
To bring down the Docker Compose containers, we just need to interrupt docker-compose
with a Ctrl+C.
^CGracefully stopping... (press Ctrl+C again to force)
Stopping gitea-postgres_gitea_1 ... done
Stopping gitea-postgres_db_1 ... done
$
Traefik proxy with GO backend
The second example uses Traefik to set up a reverse proxy that includes a monitoring dashboard. The YAML file used for this example is as follows:
1. version: "3.7"
2. services:
3. frontend:
4. image: traefik:2.2
5. command: --providers.docker --entrypoints.web.address=:80 --providers.docker.exposedbydefault=false
6. ports:
7. # The HTTP port
8. - "80:80"
9. volumes:
10. # So that Traefik can listen to the Docker events
11. - /var/run/docker.sock:/var/run/docker.sock
12. depends_on:
13. - backend
14. backend:
15. build: backend
16. labels:
17. - "traefik.enable=true"
18. - "traefik.http.routers.go.rule=Path(`/`)"
19. - "traefik.http.services.go.loadbalancer.server.port=80"
This YAML differs from the previous one in two specific ways. The first is on line 11. It mounts the Docker socket. In this case, it is a Podman socket; more specifically, it is a symlink to the Podman socket. For this example to work unchanged, we need to ensure SELinux is disabled by temporarily setting setenforce
to 0. The second interesting difference is on line 15. The moniker of build indicates that Compose should use a Dockerfile to build the image in question.
Like the previous example, I begin by cd'ing into treafix-golang
and issuing the docker-compose up
command.
$ sudo docker-compose up
5b12c4c61cdc18b2d0153badba765dc411d89ba6a447a4bee717d11aa4fd3489
6f8d2fd647295532be05a53d0556e7a2d743206c1719f1cbd2a8292b0eff6201
Untagged: docker.io/gitea/gitea:latest
Untagged: docker.io/library/postgres:alpine
...
Deleted: f965f5a1fff83fd0c440bb12a20f14e40f0ad33f1dc6d4f57736751ded8c5f87
Building backend
STEP 1: FROM golang:1.13 AS build
Completed short name "golang" with unqualified-search registries (origin: /etc/containers/registries.conf)
Getting image source signatures
Copying blob sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797
Copying blob sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064
Copying blob sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551
Copying blob sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab
Copying blob sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e
Copying blob sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7
Copying blob sha256:16b5038bccc853e96f534bc85f4f737109ef37ad92d877b54f080a3c86b3cb3a
Copying config sha256:d6f3656320fe38f736f0ebae2556d09bf3bde9d663ffc69b153494558aec9a79
Writing manifest to image destination
Storing signatures
STEP 2: WORKDIR /compose/hello-docker
Here you can see that the image build is beginning.
STEP 8: COMMIT traefik-golang_backend
Getting image source signatures
Copying blob sha256:441ea93dc2fe3d66f68f9a1a2fe84883dd643e160d6a9679f231bf21df930ed9
Copying config sha256:e0de5e88fedb487ed0e32057578fb3d64a2ef96e9b770dc3c6f5862af7592e13
Writing manifest to image destination
Storing signatures
--> e0de5e88fed
e0de5e88fedb487ed0e32057578fb3d64a2ef96e9b770dc3c6f5862af7592e13
Successfully built e0de5e88fedb
WARNING: Image for service backend was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Pulling frontend (traefik:2.2)...
f965f5a1fff83fd0c440bb12a20f14e40f0ad33f1dc6d4f57736751ded8c5f87: pulling image () from docker.io/library/traefik:2.2
Creating traefik-golang_backend_1 ... done
Creating traefik-golang_frontend_2 ... done
Attaching to traefik-golang_backend_1, traefik-golang_frontend_2
backend_1 | WARNING: no logs are available with the 'k8s-file' log driver
frontend_2 | WARNING: no logs are available with the 'k8s-file' log driver
The instructions provided by the Git repository say we can test this instance by performing an HTTP GET against localhost.
$ curl localhost
## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
{ / ===-
\______ O __/
\ \ __/
\____\_______/
Hello from Docker!
Podman worked seamlessly with Docker Compose. Nice!
Caveats
One known caveat is that Podman has not and will not implement the Swarm function. Therefore, if your Docker Compose instance uses Swarm, it will not work with Podman.
Wrap up
Docker Compose is a well-known and used application for orchestrating containers on a local container runtime. We are excited to add it as a supported application with Podman 3.0 and would love your feedback from trying it out. It would be great to see how it works for you or if you have problems (which you can report here.)
With the 3.0 release, Podman can now work nicely with Docker Compose to orchestrate containers, which is a huge step toward daemonless container management on Linux.
[ Getting started with containers? Check out this free course. Deploying containerized applications: A technical overview. ]
![From Docker Compose to Kubernetes with Podman](/sysadmin/sites/default/files/styles/card/public/2021-01/DockertoK8s.jpg?itok=o3Tyl_zI)
![grey seal lying on beach](/sysadmin/sites/default/files/styles/card/public/2019-10/grey-seal-3281160_1280.jpg?itok=kA9PBhEG)
![Moving from docker compose to podman pods](/sysadmin/sites/default/files/styles/card/public/2020-07/butterflies-807551_1920%20Cropped.jpg?itok=DiFVPx5X)
![Author’s photo](/sysadmin/sites/default/files/styles/user_picture_square/public/pictures/2019-10/latitude.jpg?itok=qxT9Q8MK)
Brent Baude
Brent is a Principle Software Engineer at Red Hat and leads the Container Runtimes team which includes things like Podman and Buildah. He is a maintainer of Podman upstream and a major contributor as well. More about me