Last weekend I posted about Docker Desktop Alternatives for macOS, and it looks like I missed one as the week before Podman release version v3.3.0 which introduced support for macOS by allowing you to launch a Podman managed virtual machine.

So, What is Podman?

Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. Containers can either be run as root or in rootless mode.

As it has been built to run and manage OCI compliant container images it means that any images want to run built using any other Container engine, like Docker or containerd (which was covered in last weeks post using Lima) will also “just” work with Podman - to the point where the Podman developers are confident enough to claim that you add alias docker=podman on your machine as running Podman is nearly indistinguishable from running Docker.

Let’s put that to the test.

Installing Podman

As I am running macOS installing Podman using Homebrew is just a simple command:

Install Podman
brew install podman

Using Podman Machine

Once the podman client binaries are installed there isn’t much you can do with it other than connect to remote instances of Podman, this is because macOS is not able to natively run containers so we need to download a Linux image to run a virtual machine, to do this simply run the following command:

Download the image
podman machine init

As you can see from the following output this downloads a custom Fedora image based on CoreOS:

Output
Downloading VM image: fedora-coreos-34.20210904.1.0-qemu.x86_64.qcow2.xz: done
Extracting compressed file

Now that we have a copy of the image and the virtual machine has been created, we can now start the virtual machine by running:

Start the machine
podman machine start

This gives the following output, as you can see, there is a warning - but this release is hot off the press so we can forgive that:

Output
INFO[0000] waiting for clients...
INFO[0000] listening tcp://0.0.0.0:7777
Waiting for VM ...
INFO[0000] new connection from  to /var/folders/q8/dplpdx9n7lz1r3m282_vcz440000gn/T/podman/qemu_podman-machine-default.sock
qemu-system-x86_64: warning: host doesn't support requested feature: CPUID.80000001H:ECX.svm [bit 2]

Once the virtual machine is up and running you can run the following command to open a shell to it:

SSH to the machine
podman machine ssh

Now we have a shell on the machine lets look at the operating system information:

Output
Connecting to vm podman-machine-default. To close connection, use `~.` or `exit`
Warning: Permanently added '[localhost]:50983' (ECDSA) to the list of known hosts.
Fedora CoreOS 34.20210904.1.0
Tracker: https://github.com/coreos/fedora-coreos-tracker
Discuss: https://discussion.fedoraproject.org/c/server/coreos/

[[email protected] ~]$ cat /etc/*release
Fedora release 34 (Thirty Four)
NAME=Fedora
VERSION="34.20210904.1.0 (CoreOS)"
ID=fedora
VERSION_ID=34
VERSION_CODENAME=""
PLATFORM_ID="platform:f34"
PRETTY_NAME="Fedora CoreOS 34.20210904.1.0"
ANSI_COLOR="0;38;2;60;110;180"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:34"
HOME_URL="https://getfedora.org/coreos/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-coreos/"
SUPPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
BUG_REPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=34
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=34
PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
VARIANT="CoreOS"
VARIANT_ID=coreos
OSTREE_VERSION='34.20210904.1.0'
DEFAULT_HOSTNAME=localhost
Fedora release 34 (Thirty Four)
Fedora release 34 (Thirty Four)
[[email protected] ~]$ exit
logout
Connection to localhost closed.

While you can open a shell to the machine it is not needed to run so let’s run some containers using the podman client on our Mac.

Using Podman

So now we have everything up and running let’s re-run the same command we used when testing Lima in the last post.

Hello World!!!

First up is the podman equivalent of running docker container run hello-world, as we have already stated that you can simply replace Docker with Podman it will be no surprise that the command is:

Podman Hello World
podman container run hello-world

As expected, this connects straight do the Docker Hub, pulls and then runs the image:

Output
Resolved "hello-world" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/hello-world:latest...
Getting image source signatures
Copying blob sha256:b8dfde127a2919ff59ad3fd4a0776de178a555a76fff77a506e128aea3ed41e3
Copying blob sha256:b8dfde127a2919ff59ad3fd4a0776de178a555a76fff77a506e128aea3ed41e3
Copying config sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726
Writing manifest to image destination
Storing signatures

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

So far, so good.

Building and running an Image

Next, lets take the Dockerfile we built last time and try building it using Podman, as a reminder the Dockerfile looks like the following:

Build the image
FROM alpine:latest
LABEL maintainer="Russ McKendrick <[email protected]>"
LABEL description="This example Dockerfile installs NGINX."
RUN apk add --update nginx && \
        rm -rf /var/cache/apk/* && \
        mkdir -p /tmp/nginx/

COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/default.conf /etc/nginx/conf.d/default.conf
ADD files/html.tar.gz /usr/share/nginx/

EXPOSE 80/tcp

ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

To build the image you will need to checkout this repo and run the build command from the folder containing the Dockerfile. The command to build and tag the image is no different to the one which we would use in Docker:

Build the image
podman image build --tag local:podman-example .

The output of the build is below:

Output
STEP 1/10: FROM alpine:latest
Resolved "alpine" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/alpine:latest...
Getting image source signatures
Copying blob sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e
Copying blob sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e
Copying config sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab
Writing manifest to image destination
Storing signatures
STEP 2/10: LABEL maintainer="Russ McKendrick <[email protected]>"
--> 9e7e28e77e9
STEP 3/10: LABEL description="This example Dockerfile installs NGINX."
--> aa67eedf7b2
STEP 4/10: RUN apk add --update nginx &&         rm -rf /var/cache/apk/* &&         mkdir -p /tmp/nginx/
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
(1/2) Installing pcre (8.44-r0)
(2/2) Installing nginx (1.20.1-r3)
Executing nginx-1.20.1-r3.pre-install
Executing nginx-1.20.1-r3.post-install
Executing busybox-1.33.1-r3.trigger
OK: 7 MiB in 16 packages
--> 6a61a4653e9
STEP 5/10: COPY files/nginx.conf /etc/nginx/nginx.conf
--> f3a1faa6b75
STEP 6/10: COPY files/default.conf /etc/nginx/conf.d/default.conf
--> 4a57c732f37
STEP 7/10: ADD files/html.tar.gz /usr/share/nginx/
--> 2180b8083f4
STEP 8/10: EXPOSE 80/tcp
--> fe304c0d38d
STEP 9/10: ENTRYPOINT ["nginx"]
--> 4dda02ea2b7
STEP 10/10: CMD ["-g", "daemon off;"]
COMMIT local:podman-example
--> 88a4d1cbc6d
Successfully tagged localhost/local:podman-example
88a4d1cbc6dcf92cd4662da990e80125ad062c0ac99551e38c0a2e8eee9231f9

Once built you can use the following command to run the image:

Running the image
podman container run -d --name podman-example -p 8080:80 --network bridge local:podman-example

Once running, open a browser and goto http://localhost:8080/ and you should be greeted by:

You know the rules and so do I

Info

Now the keen eyed amongst you may have noticed that the command to launch the container is a little slightly different than want we used in the last post, at the time of writing –network bridge is needed to map port 8080 from the virtual machine to 127.0.0.1 on the mac. The next release which is due in the next fortnight should resolve this.

Other commands

From as you can see from the terminals below, other commands you would just run in Docker to manage your containers just work:

Listing the images
podman image ls
REPOSITORY                     TAG             IMAGE ID      CREATED         SIZE
localhost/local                podman-example  52add0470325  15 minutes ago  7.57 MB
docker.io/library/alpine       latest          14119a10abf4  2 weeks ago     5.87 MB
docker.io/library/hello-world  latest          d1165f221234  6 months ago    20.4 kB

Listing all the containers
podman container ls -a
CONTAINER ID  IMAGE                                 COMMAND         CREATED         STATUS                     PORTS                 NAMES
79e3825f608e  docker.io/library/hello-world:latest  /hello          37 minutes ago  Exited (0) 37 minutes ago                        lucid_chandrasekhar
8fd9aaa8febe  localhost/local:podman-example        -g daemon off;  9 seconds ago   Up 9 seconds ago           0.0.0.0:8080->80/tcp  podman-example

Stopping a container
podman container stop podman-example
ERRO[3081] accept tcp [::]:8080: use of closed network connection
podman-example

Removing a container
podman container rm podman-example

Pruning the containers
podman container prune
WARNING! This will remove all non running containers.
Are you sure you want to continue? [y/N] y
79e3825f608e5f4a0ce8dd03849ecb55d5ff03eeff063a52c9e053bcde0460fc

Pruning the images
podman image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y

Tidying up Podman

Once you have finished having a play with Podman you will need to stop the virtual machine, as you may have already guessed, to do this run the following command:

Stopping the machine
podman machine stop

Once stopped, you can start it back up again - order to remove the Virtual Machine altogether run:

Stopping the machine
podman machine rm

This will give the following prompt:

Warning

This will delete the virtual machine and everything on it, please make sure you really to do this before agreeing.

Output
The following files will be deleted:

/Users/russ.mckendrick/.ssh/podman-machine-default
/Users/russ.mckendrick/.ssh/podman-machine-default.pub
/Users/russ.mckendrick/.config/containers/podman/machine/qemu/podman-machine-default.ign
/Users/russ.mckendrick/.local/share/containers/podman/machine/qemu/podman-machine-default_fedora-coreos-34.20210904.1.0-qemu.x86_64.qcow2
/Users/russ.mckendrick/.config/containers/podman/machine/qemu/podman-machine-default.json


Are you sure you want to continue? [y/N]

Once you have delete the virtual machine you will need to run podman machine init to recreate it again.

Bonus Desktop App

While currently there is not an official native Desktop app for Podman on macOS, Victor Gama has coded and released one which can be downloaded from heyvito/podman-macos, the screens below show the application in action …

Installing Welcome to Podman Preparing ... About ... Checking out running containers Interacting with running containers

… at the time of writing of writing the application is less than a week or so old so I recommend keeping an eye on the repo for updates.

Summary

Podman has long been used on Linux machines, and the developers (RedHat) have been working on the macOS integration long before Docker announced their changes to the licensing model for Docker Desktop.

There are however some bugs, like the one mentioned, and also some functionality which needs to be in place - the biggest of which is the at the time of writing there is no native way to mount the filesystem from the host (macOS) into the virtual machine (Podman) which could be a deal breaker from some developers as this could block them from easily developing locally. However, the good thing is the Podman developers know all of this and are working hard to add the functionality on top of their already solid base.