Getting started with containers? One cool way to start is by making a container that can run an application that is unavailable on your host operating system.
First up, you need to get a container runtime installed. I prefer podman
 but docker
 will work as well. For the simplest installation experience, I would recommend the desktop variants.

Once you have those installed, you can almost always use podman
and docker
interchangeably on the command line. We will be using the command line because, at the time of writing, Docker Desktop doesn’t support building containers in the GUI.
First up, make a new directory somewhere and create a file called Containerfile
. Note the lack of extension you Windows users! Windows really doesn’t like creating files with no extension so be sure to make sure it doesn’t add .txt
or something. You may have heard of a Dockerfile
before but the two filenames are also interchangable. However, you may have a lot more luck googling “Dockerfile
” because it has been around longer. As a quick side bar, we use Containerfile
because all the tools support a shared specification created by the Open Container Initiative (OCI) called the OCI Image Format Specification. If you want to learn more, you should probably wait, but you can go here if you really want to :).
Creating the Containerfile
OK, so now you have your file. At the top of the file, add the following line:
FROM fedora
What this is saying is we are going to base our container on an image of Fedora, a Linux distribution. In the above, FROM
is the instruction and is typically capitalized. We could also use a smaller base image like fedora-minimal
but we are going as simple as possible for this tutorial.
Next, we need to add our cool app and we use the Fedora package manager dnf
to do so. You may have used a package manager in the past (e.g. brew
, apt
, choco
, npm
, pip
) but did not realize that this is a whole class of software. If you want to learn about them, they are really interesting, you can check out the Wikipedia page. Now add the following line:
RUN dnf install -y cowsay
As you might imagine, dnf
won’t install something without confirmation by default. As a result, we need to pass -y
to get it to install without confirmation. Why do we need this you may ask? Well, when we build our container it is not an interactive process so we can’t say “Yes” to dnf
.
Now you may ask, what is cowsay
well, you can look for yourself, or wait for the reveal!!
Now let’s do the last line of our Containerfile
. Here it is:
ENTRYPOINT ["cowsay"]
Here we have what the container should do when it launches. Specifically, we want the container to launch cowsay
and pass it any parameters that were on the command line. We will get to more on that in a minute.
Building the Container Image
OK, now save your file and open a command line. Let’s build the container.
podman build -t simple-cowsay .
As I said before, you can swap podman
for docker
depending on what you installed. Fair warning, this might take awhile, might be time for a coffee. You should now see an output like the following (not exactly but similar):
STEP 1/3: FROM fedora
STEP 2/3: RUN dnf install -y cowsay
Updating and loading repositories:
Fedora 38 - x86_64 - Updates 100% | 372.7 KiB/s | 11.4 MiB | 00m31s
Fedora 38 openh264 (From Cisco) - x86_ 100% | 899.0 B/s | 6.5 KiB | 00m07s
Fedora 38 - x86_64 100% | 2.1 MiB/s | 34.5 MiB | 00m16s
Repositories loaded.
Package Arch Version Repository Size
Installing:
cowsay noarch 3.7.0-7.fc38 fedora 72.5 KiB
Installing dependencies:
groff-base x86_64 1.22.4-11.fc38 fedora 3.8 MiB
<snip />
perl-vars noarch 1.05-497.fc38 updates 4.8 KiB
Installing weak dependencies:
perl-NDBM_File x86_64 1.15-497.fc38 updates 29.7 KiB
Transaction Summary:
Installing: 62 packages
Downloading Packages:
[ 1/62] perl-Carp-0:1.52-490.fc38.noarc 100% | 13.2 KiB/s | 28.8 KiB | 00m02s
<snip />
[62/62] perl-NDBM_File-0:1.15-497.fc38. 100% | 30.1 KiB/s | 23.6 KiB | 00m01s
--------------------------------------------------------------------------------
[62/62] Total 100% | 321.9 KiB/s | 8.5 MiB | 00m27s
Verifying PGP signatures
Running transaction
[1/2] Verify package files 100% | 1.6 KiB/s | 62.0 B | 00m00s
[2/3] Prepare transaction 100% | 2.3 KiB/s | 62.0 B | 00m00s
[3/4] Installing ncurses-0:6.4-3.202301 100% | 38.1 MiB/s | 624.1 KiB | 00m00s
<snip />
>>> Stop trigger-install scriptlet: glibc-common-0:2.37-1.fc38.x86_64
--------------------------------------------------------------------------------
[64/64] Total 100% | 54.3 MiB/s | 30.1 MiB | 00m01sRemoved 82 files, 11 directories. 0 errors occurred.
--> e010c8ceb6f7
STEP 3/3: ENTRYPOINT ["/bin/cowsay"]
COMMIT simple-cowsay
--> 0afbf280e77a
Successfully tagged localhost/simple-cowsay:latest
Running the Container
Once that completes, let’s run it!
podman run -it simple-cowsay 'Cows are AWESOME!!'
Now you can play with it to your hearts content!
A Little Bit Extra
If you want to do a slightly more advanced version, you can also clean up the container after the installation of cowsay
. When you create a container image, each line in the Containerfile
is independent and creates a layer that is carried through to the final image. When we install things in the container image using the package manager, the package manager has to download all the dependency information to figure out what your the thing you want to install needs. As you might imagine, this can be a lot of content and it is a permanent part of the container image after that layer. As a result, it is recommended that you remove the content you don’t need after the installation. In order to do that we add dnf clean all -y
to the installation line. We need to replace the installation line like this:
RUN dnf install -y cowsay && dnf clean all
The RUN
 instruction is just the equivalent of running the command in Linux. In Linux, the &&
 just links two commands together and if the first one is successful, the second one runs. Now before you rebuild the container, run podman images
 and note the size of your simple-cowsay
 container. Now rebuild the container and see how much smaller it is by running podman images
 again.
The Whole Shebang
The whole file, just in case:
FROM fedora
RUN dnf install -y cowsay && dnf clean all
ENTRYPOINT ["cowsay"]