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:
- Only one machine is active at a time.
- It's possible to name a machine on creation.
- 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:
- You bind a Mac directory to a directory of the machine.
- 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:
- Run Podman and the Podman default machine as root.
- Create an alias to invoke
podman
using the worddocker
. - 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. ]
About the author
Marcelo Paternostro is a Senior Software Developer at Datadog. He was previously a Principal Engineer at Oracle, and also worked at IBM. For more than 20 years, Marcelo has been implementing software development tools, including VS Code extensions, browser IDEs, and Eclipse Components - in fact, he’s one of the authors of the 2nd edition of the Eclipse Modeling Framework (EMF) book.
In the development tools domain, Marcelo has used and mastered many different technologies and techniques, including several data modeling standards, distributed software architectures, and infrastructure components. He is always keeping an eye on how new ideas can improve the workflows of developers and how to make writing good software a fun and rewarding activity. One of his most frequent sayings is that “simple things should be simple, complex things should be possible”: a motto that requires great attention and innovation to offer solutions that cater both to beginners and seasoned developers.
When he’s not on a programming spree marathon or a "hack-an-idea" session, Marcelo enjoys spending time with his wife, son, daughter, and friends. He’s also a martial artist, video game player, and an avid searcher of good restaurants.
Browse by channel
Automation
The latest on IT automation for tech, teams, and environments
Artificial intelligence
Updates on the platforms that free customers to run AI workloads anywhere
Open hybrid cloud
Explore how we build a more flexible future with hybrid cloud
Security
The latest on how we reduce risks across environments and technologies
Edge computing
Updates on the platforms that simplify operations at the edge
Infrastructure
The latest on the world’s leading enterprise Linux platform
Applications
Inside our solutions to the toughest application challenges
Original shows
Entertaining stories from the makers and leaders in enterprise tech
Products
- Red Hat Enterprise Linux
- Red Hat OpenShift
- Red Hat Ansible Automation Platform
- Cloud services
- See all products
Tools
- Training and certification
- My account
- Customer support
- Developer resources
- Find a partner
- Red Hat Ecosystem Catalog
- Red Hat value calculator
- Documentation
Try, buy, & sell
Communicate
About Red Hat
We’re the world’s leading provider of enterprise open source solutions—including Linux, cloud, container, and Kubernetes. We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.
Select a language
Red Hat legal and privacy links
- About Red Hat
- Jobs
- Events
- Locations
- Contact Red Hat
- Red Hat Blog
- Diversity, equity, and inclusion
- Cool Stuff Store
- Red Hat Summit