Skip to main content

How to run containers on Mac with Podman

Go beyond the basics, learn what happens under the hood when running Podman on your Mac, and create a flexible container environment that meets your needs.
Image
Laptop with pineapple on a wooden table

Image by Scott Webb from Pixabay

Containers are a powerful technology used in most of the systems the world uses everyday. Containers also allow savvy users to better use the amazing hardware at their disposal without worrying about the operating system's stability when tackling tasks that require software installations or complicated configurations.

This article shows how to run containers on your Mac using Podman. My goal is to go beyond the basics to expose some of what happens under the hood. Hopefully this knowledge will help you as it helped me: I now have several container environments on my laptop, each targeting a specific use case, with minimum waste.

[ Download now: Podman basics cheat sheet ]

How I use containers in this article is intentionally simple to focus on the container technology.

Why Podman

The rise of containers is intrinsically associated with Docker, to the point that many see them as synonyms. While not discrediting the great work done by Docker, I am happy there are alternatives like Podman pushing the technology and creating a diverse ecosystem. Podman offers everything Docker does, plus a great deal of flexibility and control, which I've learned to appreciate.

Here's a recipe to get things going:

# 1. Install Podman using Brew.
brew install podman

# 2. Check Podman's version.
#    (should be greater than 4.0.0)
#    (this article was written with 4.4.1)
podman --version

# 3. Start Podman's machine.
podman machine init \
  --now

# 4. Run the hello-world container.
podman run \
  --rm \
  hello-world

If most of the steps above are strange to you, I recommend investing some time to learn about Brew and containers in general. However, if you are scratching your head only about step 3, you are in the right place.

[ Learn more about how to replace Docker with Podman on a Mac. ]

The Podman machine

Containers are a Linux technology. I could go deeper into details to justify this assertion, but let's just take it as an absolute truth.

"If containers are a Linux technology, how can I run them on Mac or Windows?"

The answer to this question is "well... you can't." When using other platforms, the only way to run containers is to rely on a Linux virtual machine (VM). There is no way around this.

The approach Docker chose to address this limitation is to hide the VM installation and configuration, creating an illusion that everything runs natively. In my opinion, this was a great idea: it helped promote the use of containers, which was really the point a while ago. Advanced users would inevitably find out about the VM and stumble on its configuration options.

[ Related reading: How to copy Docker images to Podman ]

Podman chose a different route. With the maturity and wide adoption of containers, surfacing the VM details is not risky anymore. In reality, exposing it may enable more sophisticated solutions as it increases awareness of a key architectural aspect of the technology.

Cutting the philosophical discussion short, when you run podman machine init --now, you create and (because of the --now argument) immediately start the VM that Podman uses to run the containers on your Mac. From then on, every container-related command, like podman run --rm hello-world, is executed within this VM.

With great flexibility comes great powers

"This machine command seems like an annoyance... Why not hide it?"

This question is pertinent for basic, potentially simplistic, uses. I'd argue though that, even with the most basic cases, being aware of the machine has benefits. For example, just by knowing that the machine exists, you can choose to shut it down to free resources on your host whenever you're not using containers.

# Stop the machine used by Podman.
podman machine stop

Another interesting benefit of having easy access to the machine is that you can tweak it for a specific usage, such as one that requires more computational power.

# Remove the previous machine.
# (must be stopped)
#
# Important: this command removes all containers and images you
#            may have if this is not the first time you are
#            using Podman on your Mac.
podman machine rm

# Create and start a machine with custom settings.
podman machine init \
  --cpus 4 \
  --memory 2048 \
  --disk-size 100 \
  --now

Now your containers run in a considerably more powerful VM. You can read about the machine init options in Podman's documentation.

If you run a container after the commands above, like hello-world used before, notice the output of the podman run ... command. The very first lines show you yet another behavior that you could have missed if the machine was hidden: The container image was downloaded again, which is a consequence of images being stored in the machine. You also know that you are not leaving any bytes behind after removing a machine using podman machine rm.

I want more!

As you use machines, you might consider having many of them, with each tweaked for a specific use. I have great news if that happens: Managing machines is 100% supported by Podman.

Podman's machine management works as follows:

  1. Only one machine is active at a time.
  2. It's possible to name a machine on creation.
  3. A connection exposes a machine to Podman's commands.

With commands:

# If needed, stop the active machine.
podman machine stop

# Create and start a machine named "development".
podman machine init \
  --now \
  development

# Set the connection for the "development" machine as the
# default connection.
podman system connection default development

The last command introduces Podman's connections. In the same way you can use the podman program to interact with containers running within a VM on your laptop, you can also use it to interact with containers running remotely. For this to work, you must register these "places where containers are running" as connections. I will describe connections later this article.

[ Get hands on with Podman in this tutorial scenario. ]

Back to Podman machines, after running the commands above, a command like podman run targets the development machine. If you need another machine, such as one closer to the testing environment, you can create it and switch between the existing machines whenever necessary.

# Create a machine named "testing".
podman machine init \
  --cpus 4 \
  --memory 2048 \
  --disk-size 100 \
  testing

# Stop the "development" machine.
podman machine stop development

# Set the connection for the "testing" machine as the
# default connection.
podman system connection default testing

# Start the "testing" machine.
podman machine start testing

You can change the characteristics of a stopped machine using the podman machine set command. This can be very helpful when you need to run tasks with a different configuration. It can be also useful when adapting an existing machine to reflect a change made to the environment the machine is emulating.

[ Keep your most commonly used commands handy with the Linux commands cheat sheet. ]

Use the default machine

Now that you know about named machines, you may wonder about the difference between these commands:

# 1. Create machine named "development".
podman machine init development

# 2. Create machine with the default name.
podman machine init

Command #2 creates a machine with the default name, which is the machine affected by a Podman machine command executed without a machine name. In other words, a command like podman machine... applies to a named machine only if the name is passed as an argument.

# List all machines.
podman machine list

# Show details about the "development" machine.
podman machine inspect development

# Stop the "testing" machine.
podman machine stop testing

# Start the default machine.
podman machine start

# Stop the default machine.
podman machine stop

# Start the "development" machine.
podman machine start development

Use connections

As mentioned before, you can have multiple machines but only one can be active at a time. Also, the term "default machine" really means "a machine with the default name," or the machine whose name you don't need to specify when running a podman machine command.

Connections behave a little differently. Like machines, you can have multiple connections and one can be set as the default connection. However, unlike machines, you can interact with the containers exposed by any connection at any moment. In fact, whenever you run a command applicable to containers and images, you are running this command on the environment referenced by a connection; if no connection is specified, your command uses the default connection.

The command podman system connection list lists all connections. If you look at its output, you will notice that Podman has automatically registered connections for each machine you created.

Say your default connection is associated with the testing machine and the development machine is the currently active one. What happens when you run the following commands?

# 1. Should list the images of the default connection.
podman images

# 2. List the images of the development connection.
podman \
  --connection development \
  images

Command #1 fails because the environment referenced by the default connection (testing machine) is not active. Command #2 works because the connection development references an active environment, the development machine.

This explains why all recipes in this article include podman system connection default ...: while optional, it's easier to use Podman's commands when targeting the default connection. This is even more convenient if your main usage is interacting with the active machine. However, if you can SSH into servers running containers with Podman, you can register connections to those servers using podman system connection add ... and then interact with their containers from your Mac. In this case, it may be more productive to specify the connection as a command argument, as in the Command #2 above, without redefining the default connection each time.

[ Want to test your sysadmin skills? Take a skills assessment today. ]

Mount files

Since version 4.0, Podman offers all tools required to use containers on scenarios that involve accessing files located on a Mac.

"Is it as simple as Docker?"

It can be! However, it can also be far more precise, allowing fine control over the files exposed to the containers running on a machine.

This is how a container can access files on your Mac:

  1. You bind a Mac directory to a directory of the machine.
  2. The containers running on that machine can access any file inside that Mac directory, which is exposed through the machine's directory.

Illustrating the process with commands:

# 1. If needed, stop any running machine.
#    (using "testing" as an example)
podman machine stop testing

# 2. Create and start a machine named "work" with access to the
#    projects subdirectory of the Mac's home directory.
podman machine init \
  -v "${HOME}/projects":/shared/projects \
  --now \
  work

# 3. Set the connection to the "work" machine as the
#    default connection.
podman system connection default work

# 4. Run a container that lists the content of a Mac directory,
#    exposed via the machine's directory.
podman run \
  --rm \
  -v /shared/projects/shopping-cart/js:/app \
  alpine:latest \
  ls -la /app

Step #2 creates the work machine with the -v option binding the Mac directory ~/projects to the machine directory /shared/projects. Then, in Step #4, the -v option of the run command binds the machine directory /shared/projects/shopping-cart/js to the container's directory /app. So, indirectly, the container's /app directory references the Mac directory ~/projects/shopping-cart/js.

Now, while using the work machine, every container can access the files under the Mac ~/projects directory.

"What about the 'it can be' as simple as Docker comment?"

When you create a machine on Mac without any explicit binding, Podman automatically binds it to a few directories, including /Users, which contains the home directory of all users. Moreover, the path of these automatic bindings is the same on Mac and on the machine, so you can use the Mac local path to refer to the binding path when running containers. With commands:

# 1. If needed, stop any running machine.
#    (using "work" as an example)
podman machine stop work

# 2. Create and start a machine named "tools" without
#    any bindings.
podman machine init \
  --now \
  tools

# 3. Set the connection to the "tools" machine as the default connection.
podman system connection default tools

# 4. Run a container that lists the content of the Mac /Users directory.
podman run \
  --rm \
  -v /Users:/Users \
  alpine:latest \
  ls -la /Users

Although tempting, don't let the apparent simplicity of Command #4 deviate you from what is really happening. That command involves three different directories named /Users that happen to point to the same area of your disk: the ls command lists the container's /Users directory, which is bound to the /Users of the tools machine, which is (finally) bound to the /Users on your Mac.

[ Related reading: Podman Compose or Docker Compose: Which should you use in Podman? ]

For more information on this topic, check the help of the command podman machine init, which lists all directories that are automatically bound to the machine.

Summary

The straightforward way you can define which files are available to containers combined with the ability to manage multiple machines is extremely powerful. And by knowing how machines work, you can create customized, independent container environments to tackle different projects and tasks.

Personally, the strength of the previous paragraph lies on "you can": the flexibility described in this article is really optional. To the point, if you prefer to use Podman while keeping a Docker-style experience, you could:

  1. Run Podman and the Podman default machine as root.
  2. Create an alias to invoke podman using the word docker.
  3. Use the automatic bindings.

I am not providing a recipe here because I have never tried this, so I'm not sure I'd list all steps required to make Podman behave like Docker on Mac. Moreover, as stated many times throughout this article, I see beauty in the differences between these two amazing tools and in the choices that allow me to create container environments that perfectly align with my preferences. Even if that's not your case, I hope this article contributes to your understanding of how containers work.

[ Download now: A system administrator's guide to IT automation. ]

Topics:   Containers   Podman  
Author’s photo

Marcelo Paternostro

Marcelo Paternostro is a Senior Software Developer at Datadog. He was previously a Principal Engineer at Oracle, and also worked at IBM. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.