Sign In

Podman Deep Dive: From Fundamentals to Enterprise Platform Engineering

Container technology has become the backbone of modern software development, DevOps, Platform Engineering, and cloud-native infrastructure. From Red Hat Enterprise Linux (RHEL) and Podman to Kubernetes and Red Hat OpenShift, containers power the applications that run millions of businesses worldwide. Yet, becoming a successful Linux or Platform Engineer is not about memorizing commands like podman run or podman exec. It is about understanding why containers exist, how they are created, what happens inside the Linux kernel, and how to troubleshoot them in real-world production environments. This guide is written for learners who want to move beyond tutorials, develop an engineer's mindset, and build the practical knowledge required to confidently work with enterprise container platforms.


What You'll Learn in This Series

Throughout this series, you will build a solid understanding of the complete Podman ecosystem, from container fundamentals to enterprise administration. The topics covered include:

  • Podman Fundamentals - Architecture, Container Lifecycle, Images, Containers, Containerfiles, Image Registries
  • Linux Internals - OverlayFS, Namespaces, cgroups, OCI Runtime (crun/runc), conmon, Process Isolation.
  • Storage, Networking & Security - Volumes, Bind Mounts, Storage Drivers, Rootless Containers, SELinux, Seccomp, Container Networking.
  • Pods & Kubernetes Integration - Pods, Multi-Container Applications, podman generate kube, podman play kube, Kubernetes & OpenShift Mapping.
  • Enterprise Administration - Buildah, Skopeo, systemd, Quadlets, Image Management, Automation, Monitoring.
  • Troubleshooting & Best Practices - Production Troubleshooting, Performance Tuning, Security Hardening, Interview Questions, Real-World Use Cases .

By the end of this series, you'll understand not only how to use Podman, but also how it works internally and how its architecture integrates with Kubernetes and Red Hat OpenShift. 

1.   Podman Fundamentals

Before running your first container, it's essential to understand the core concepts that make Podman work. This chapter introduces the fundamentals of container technology, explains the architecture of Podman, and builds the foundation required to understand container execution, Linux internals, and enterprise container platforms such as Kubernetes and Red Hat OpenShift.

1.1 Why Do We Need Containers?

Imagine a software developer building an application on a Red Hat Enterprise Linux 9 system. After writing the code, the developer installs the required libraries, runtime, configuration files, and other dependencies needed for the application. Once development and local testing are complete, the application works perfectly on the developer's machine.


The next step is to hand the application over to the testing team. Although the tester may also be using Red Hat Enterprise Linux, the system is often configured differently. Different library versions, missing dependencies, environment variables, or configuration settings can cause the application to behave unexpectedly or even fail to start. This is commonly known as an environment inconsistency problem.


One traditional solution is to create a Virtual Machine (VM) that replicates the developer's environment. While this approach provides consistency, each virtual machine requires its own operating system, CPU, memory, storage, and ongoing maintenance. As the number of applications grows, managing dozens or even hundreds of virtual machines become expensive, resource-intensive, and operationally complex.

Podman containerization workflow comparing traditional virtual machines with containers, showing how container images eliminate environment inconsistencies between developers and testers.
Fig 1.1 Containerization-vs-Virtual-Machine-Workflow

Container technology was introduced to solve these challenges. Instead of recreating an entire operating system for every application, the developer packages the application together with all its required libraries, dependencies, runtime, and configuration into a Container Image . This image is stored in a Container Registry , from where testers, developers, or production systems can download and run the same application in a consistent environment.


By using the same container image across development, testing, and production, organizations eliminate environment inconsistencies, reduce infrastructure overhead, improve resource utilization, and ensure that the application behaves consistently throughout its lifecycle.


However, before understanding how a container image becomes a running container, we must first understand two fundamental concepts of the Linux operating system: User Space and Kernel Space. These concepts form the foundation of container technology and will help us understand how Podman interacts with the Linux kernel to create isolated container environments.



1.2 Linux Filesystem, User Space, and Kernel Space

The Linux filesystem starts from a single root directory (/), and every file and directory in the operating system exists under this root. When we browse the root directory, we find folders such as /bin, /usr, /lib, /etc, /home, and /var, each serving a specific purpose. However, to understand container technology, it is more important to understand how Linux is logically divided into User Space and Kernel Space rather than focusing only on the directory structure.


In the previous section, we learned that containers solve the environment inconsistency problem by packaging an application's libraries, dependencies, runtime, and configuration into a Container Image. However, an important question still remains. If the container already contains everything required to run the application, what does the host operating system provide? Does every container include its own operating system, or does it use the operating system already installed on the host machine? To answer these questions, we must first understand how Linux is organized internally into User Space and Kernel Space. This distinction is the key to understanding why containers are lightweight, efficient, and much faster than traditional virtual machines.


Before understanding containers, we must first understand how an application executes on Linux. Every application requires its executable code, libraries, runtime environment, configuration files, environment variables, and other dependencies. When a user starts an application, either from a shell or another program, the executable is loaded into memory. The application cannot communicate directly with hardware resources such as the CPU, memory, storage, or network devices. Instead, it requests services from the Linux kernel through system calls. The kernel loads the program into memory, allocates RAM, schedules CPU execution, manages file operations, controls network communication, and coordinates hardware resources before returning the results back to the application.


At this point, it is important to understand the difference between User Space and Kernel Space.


Linux User Space and Kernel Space architecture diagram showing application dependencies packaged into a Container Image, stored in a Container Registry, and executed by Podman using the host Linux kernel with isolated namespaces.
Figure 1.2: Illustration of Linux User Space and Kernel Space architecture, showing how application dependencies are packaged into a Container Image, stored in a Container Registry, and executed by Podman using the host Linux kernel to create an isolated container environment.


User Space contains everything that belongs to an application. This includes the application executable, libraries, shared libraries (.so files), runtime environment, configuration files, environment variables, user processes, and all other software components required for that specific application to run. These components are application-specific and can vary from one application to another. For example, one application may require version 1.2 of a library, while another application may require version 1.3 or an entirely different dependency. This difference in software requirements is one of the primary reasons applications behave differently across development, testing, and production systems.


Kernel Space, on the other hand, contains the core services provided by the Linux operating system. The kernel is responsible for process management, CPU scheduling, memory management (RAM), file system management, network management, device drivers, hardware communication, and security enforcement. These services remain common for every application running on the host system. Whether the system is running a web server, a Java application, a Python program, or a database server, every application depends on the same Linux kernel to communicate with the underlying hardware. The kernel acts as the engine of the operating system, providing a consistent interface between software and hardware.


This separation between User Space and Kernel Space forms the foundation of container technology. The software components inside User Space change from one application to another because every application has its own libraries, runtime, configuration files, and dependencies. In contrast, the services provided by Kernel Space remain the same regardless of which application is running. Every application ultimately depends on the same kernel to access the CPU, memory, storage, networking, and other hardware resources.


Container technology takes advantage of this architecture. Instead of packaging an entire operating system, the developer packages only the User Space components required by the application, including its executable, libraries, dependencies, runtime environment, configuration files, and environment variables, into a Container Image. Since the Linux kernel is already available on the host machine, there is no need to package it inside every container. This significantly reduces image size, improves startup time, and makes containers much more lightweight than traditional virtual machines.


The completed Container Image is then stored in a Container Registry, where it can be shared with developers, testers, or production environments. A tester simply downloads the same image and runs it using Podman. During execution, Podman works with the host Linux kernel to create an isolated container environment. Although multiple containers may run different applications with different libraries and dependencies, they all share the same host Linux kernel. This architecture ensures that every application runs consistently across development, testing, and production while eliminating dependency conflicts and environment inconsistencies.


With this understanding, we now know why only the User Space is packaged into a Container Image and how the Linux kernel provides the isolated execution environment required to run it. The next step is to explore the tools that make this possible in practice.

1.3 Podman Ecosystem

In the previous sections, we learned why containers were introduced and how Linux separates User Space and Kernel Space to make containerization possible. However, understanding the Linux kernel alone is not enough to build and run containers. A complete container platform consists of several tools, each responsible for a specific task, such as building images, storing images, transferring images, and running containers. Together, these components form the Podman Ecosystem.

1.3.1 Container Image

A Container Image is a portable, read-only package that contains everything required to run an application. It includes the application code, executable binaries, libraries, runtime environment, configuration files, environment variables, and other dependencies. A container image does not contain the Linux kernel because it shares the host operating system's kernel.

Container images are immutable, meaning their contents do not change after they are built. Whenever an application needs to be updated, a new image is created rather than modifying the existing one. This ensures consistency across development, testing, and production environments.


Key Components

•Application Code 

•Executable Binaries 

•Shared Libraries 

•Runtime Environment 

•Configuration Files 

•Environment Variables 

•Metadata 

•Read-only Image Layers


Key Responsibility: Packages the application and all required software dependencies.

1.3.2 Container

A Container is a running instance of a Container Image. When a container image is executed by a container engine such as Podman, the Linux kernel creates an isolated execution environment using namespaces and cgroups. A new writable layer is added on top of the read-only image layers, allowing the application to create files and store temporary data during execution.

Although multiple containers may run on the same host, each container behaves as an independent system with its own processes, filesystem, networking, and hostname while securely sharing the same Linux kernel.


Container Image + Writable Layer + Linux Kernel = Running Container.


Key Responsibility: Provides an isolated runtime environment for the application.

1.3.3 Container Engine

A Container Engine is software that creates, runs, stops, removes, and manages containers. It acts as the bridge between the user and the Linux kernel, providing a simple interface to work with container images and containerized applications. The container engine communicates with the underlying container runtime to create isolated execution environments.

Examples

•Podman 

•Docker 

•containerd 

•CRI-O

1.3.4 Container Runtime

A Container Runtime is the low-level software responsible for creating and starting containers. It works directly with the Linux kernel to create namespaces, configure cgroups, mount the container filesystem, and start the container's main process. Users generally do not interact with the runtime directly; instead, it is invoked by a container engine such as Podman.


Examples

•crun 

•runc

1.3.5 Podman

Podman is an open-source, daemonless container engine used to build, run, manage, and deploy OCI-compliant containers and pods. It provides a secure, rootless-first approach to container management while working directly with the Linux kernel through an OCI runtime. Podman is the default container engine for Red Hat Enterprise Linux and integrates naturally with Kubernetes and Red Hat OpenShift.

1.3.6 Buildah

Buildah is an open-source tool designed specifically for building OCI-compliant container images. It creates, modifies, and builds container images from Containerfiles or directly from the command line without requiring a running container.

1.3.7 Skopeo

Skopeo is an image management tool used to inspect, copy, sign, and transfer container images between local storage and remote container registries. Unlike Podman, Skopeo works directly with container images and does not run containers.

1.3.8 CRI-O

CRI-O is a lightweight container runtime built specifically for Kubernetes and Red Hat OpenShift. It implements the Kubernetes Container Runtime Interface (CRI) and is responsible for running containers inside Kubernetes clusters.




Podman ecosystem architecture diagram showing Buildah, Skopeo, Container Registry, Podman, OCI Runtime, Linux Kernel, and the complete end-to-end container execution workflow for OCI-compliant containers.
Figure 1.3 – Podman Ecosystem: Layered Architecture and End-to-End Container Workflow


In enterprise environments, engineers rarely troubleshoot only Podman. Most issues involve multiple layers such as the container image, registry, storage, networking, SELinux, or the OCI runtime. Understanding the complete Podman ecosystem helps isolate problems more quickly and improves troubleshooting efficiency.

1.4 Podman Architecture and Internal Working

In the previous section, we explored the Podman ecosystem and the tools responsible for building, transferring, storing, and running container images. In this chapter, we will take a practical approach to understand Podman's internal architecture. Instead of only studying diagrams, we will execute Podman commands and observe how images are downloaded, stored, and managed inside the Linux filesystem. By the end of this chapter, you will clearly understand what happens internally when you execute podman pull and podman run.

1.4.1 Installing Podman (Lab Setup)

Install Podman using the following commands.

bash
sudo dnf update
sudo dnf install podman
podman version

OUTPUT

[root@Byte2Build ~]#  dnf install podman -y

Updating Subscription Management repositories.

Last metadata expiration check: 0:53:11 ago on Tue Aug 11 01:40:13 2026.

Package podman-7:5.8.2-5.el10_2.x86_64 is already installed.

Dependencies resolved.

Nothing to do.

Complete!

[root@Byte2Build ~]# podman version

Client:        Podman Engine

Version:       5.8.2

API Version:   5.8.2

Go Version:    go1.26.5 (Red Hat 1.26.5-1.el10_2)

Git Commit:    a476c2b2c35443a3db1a1668ac7b402eb1eb0619

Built:         Fri Jul 10 00:00:00 2026

Build Origin:  Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>

OS/Arch:       linux/amd64

If you are logged in as the root user, sudo is not required.

Verify the installation by checking the installed Podman version.

1.4.2 Container Registry

Before downloading our first container image, we must understand where container images are stored.

A Container Registry is a centralized repository used to store, manage, version, and distribute OCI-compliant container images. Developers build container images and push them to a registry, while testers, administrators, or production servers pull the same images whenever required.

Examples

•registry.access.redhat.com 

•registry.redhat.io 

•quay.io 

•docker.io (Docker Hub) 

•ghcr.io (GitHub Container Registry)



Byte2Build Podman Image Lifecycle diagram illustrating how a container image is built by the developer, pushed to a container registry, pulled by Podman, and stored locally before running a container.
Figure 1.4.2 - Podman Image Lifecycle: Developer → Build → Push → Registry → Pull → Podman -- Byte2Build


1.4.3 Pull Your First Container Image

Download the UBI9 HTTPD image from the Red Hat Container Registry.

bash
podman pull registry.access.redhat.com/ubi9/httpd-24:9.8-1785990300

OUTPUT

[root@Byte2Build ~]# podman pull registry.access.redhat.com/ubi9/httpd-24:9.8-1785990300

Trying to pull registry.access.redhat.com/ubi9/httpd-24:9.8-1785990300...

Getting image source signatures

Checking if image destination supports signatures

Copying blob 6c499c857aeb done   | 

Copying blob c1c483020efb done   | 

Copying blob e5cf4fb02919 done   | 

Copying config 39a151cad9 done   | 

Writing manifest to image destination

Storing signatures

39a151cad9e9f78ac5511e4b74b19f13f5ace5f3921038931fe21b6a6b25b149


Byte2Build Podman pull command diagram explaining the image reference format, including Podman command, pull action, container registry, repository, image name, and image tag used to download OCI container images.
Figure 1.4.3 - Understanding the Podman Pull Command and Image Reference Breakdown -- Byte2Build

What Happens Internally?


When the command is executed, several components work together.


Step 1 – Podman

Podman receives the pull request and parses the image name. It determines which registry to contact and checks whether the requested image already exists in the local image store.


Step 2 – Container Registry

If the image is not available locally, Podman connects to the specified container registry. The registry returns the image manifest, metadata, and the list of OCI image layers that make up the container image.


Step 3 – Skopeo (Behind the Scenes)

Although you are executing a Podman command, Podman uses image-copying libraries from the same container ecosystem to communicate with registries, retrieve image metadata, verify image manifests, and securely transfer image layers from the remote registry to the local system. This is the same image transport capability that Skopeo is designed for.


Step 4 – Buildah

Buildah is not involved during the podman pull operation.

Its responsibility is to build and modify container images, not download them. Once an image has been downloaded, Buildah can be used later to create a new custom image or modify an existing one.


Step 5 – Local Storage

After all image layers are verified successfully, Podman stores them under the local container storage.


bash
/var/lib/containers/storage/

OUTPUT
[root@Byte2Build ~]# ls -lash /var/lib/containers/storage/
total 132K
4.0K drwxr-xr-x. 9 root root 4.0K Aug 11 01:51 .
   0 drwxr-xr-x. 5 root root   50 Aug 11 01:55 ..
112K -rw-r--r--. 1 root root 112K Aug 11 01:51 db.sql
4.0K -rw-r--r--. 1 root root    8 Aug 11 01:51 defaultNetworkBackend
   0 drwx------. 2 root root    6 Aug 11 01:51 libpod
4.0K drwx------. 7 root root 4.0K Aug 11 02:33 overlay
   0 drwx------. 2 root root   29 Aug 11 01:51 overlay-containers
   0 drwx------. 3 root root  116 Aug 11 02:13 overlay-images
4.0K drwx------. 3 root root 4.0K Aug 11 01:55 overlay-layers
4.0K -rw-r--r--. 1 root root   64 Aug 11 02:33 storage.lock
   0 drwx------. 2 root root    6 Jun 17  2025 tmp
   0 -rw-r--r--. 1 root root    0 Aug 11 01:51 userns.lock
   0 drwx------. 2 root root    6 Aug 11 01:51 volumes

The downloaded image is now available locally and can be viewed using:


podman images
or
podman image list

OUTPUT
[root@Byte2Build ~]#  podman image list

REPOSITORY                                TAG             IMAGE ID      CREATED     SIZE
registry.access.redhat.com/ubi9/httpd-24  9.8-1785990300  39a151cad9e9  4 days ago  310 MB

Podman also assigns a unique Image ID : 39a151cad9e9 which identifies the downloaded OCI image in the local storage.



1.4.4 Exploring Podman Image Storage Architecture

After successfully downloading the container image, Podman stores it inside the local container storage.  Before exploring the filesystem, let's first understand how the downloaded image is organized internally.

Display the image layer hierarchy:

bash
podman image tree registry.access.redhat.com/ubi9/httpd-24:9.8-1785990300
or
podman image tree IMAGE_ID

Examples :
podman image tree 39a151cad9e9


OUTPUT
[root@Byte2Build ~]# podman image tree 39a151cad9e9
Image ID: 39a151cad9e9
Tags:     [registry.access.redhat.com/ubi9/httpd-24:9.8-1785990300]
Size:     309.9MB
Image Layers
├── ID: f2890a7189ea Size: 219.5MB
├── ID: 578fa8fdb023 Size: 57.93MB
└── ID: 7b9a05f89521 Size: 32.31MB Top Layer of: [registry.access.redhat.com/ubi9/httpd-24:9.8-1785990300]

The output displays the complete parent-child relationship between the OCI image layers. Instead of a single large file, a container image is built from multiple read-only layers, where each layer represents a specific filesystem change. These layers are stacked together to form the final container image.

At this stage, you may ask:

Where are these layers stored on the Linux host?

To answer this, navigate to the Podman storage directory.

bash
cd /var/lib/containers/storage
tree -L 1

OUTPUT
[root@Byte2Build ~]# cd /var/lib/containers/storage
tree -L 1
.
├── db.sql
├── defaultNetworkBackend
├── libpod
├── overlay              # Container filesystem( Conatiner Storage and OverlayFS Structures)
├── overlay-containers   # Container Metadata
├── overlay-images       # Image Metadata
├── overlay-layers       # Layer Metadata
├── storage.lock
├── tmp
├── userns.lock
└── volumes              # Persistent Volumes
8 directories, 4 files
  • overlay/ : Stores the actual image filesystem layers, writable container layers, merged filesystem, and OverlayFS working directories.
bash
ls -lash /var/lib/containers/storage/overlay/

 This is where our investigation begins to connect with the next chapter 1.4.5.


  • overlay-images/ :Maps each Image ID to its OCI image metadata, manifest, repository, and tags.
bash
ls -lash /var/lib/containers/storage/overlay-images/

OUTPUT
[root@Byte2Build ~]#
 ls -lash /var/lib/containers/storage/overlay-images/
total 36K
4.0K drwx------. 6 root root 4.0K Aug 13 10:03 .
4.0K drwx------. 8 root root 4.0K Aug 13 06:27 ..
4.0K drwx------. 2 root root 4.0K Aug 13 06:27 26b2eb03618e749084668eaff68cff8f81dda12d06ac641be7a6398b82a6f25b
4.0K drwx------. 2 root root 4.0K Aug 13 06:27 321674f9d314894eb53e6be561f9beac35969782bf43d56a01ffef7c492792c3
4.0K drwx------. 2 root root 4.0K Aug 13 10:03 39a151cad9e9f78ac5511e4b74b19f13f5ace5f3921038931fe21b6a6b25b149
4.0K drwx------. 2 root root 4.0K Aug 13 06:27 e6ebb27dc26da18a0be15e06cc98f64a0a3ad3ba2ad5a5691dc6b10ef22a3da1
8.0K -rw-------. 1 root root 7.6K Aug 13 10:03 images.json
4.0K -rw-r--r--. 1 root root   64 Aug 13 10:03 images.lock
The important observation is the long identifier beginning with 39a151cad9e9f78ac5... the Image ID returned when the image was pulled.


  • overlay-layers/ :Stores metadata describing every image layer and the parent-child relationship displayed by podman image tree.
bash
ls -lash /var/lib/containers/storage/overlay-layers/

Think about the relationship like this:     podman image tree → Logical image layer view (Layer A → Layer B → Layer C) → layer metadata stored under overlay-layers/.

So, podman image tree shows us the layer hierarchy; overlay-layers/ is part of the storage metadata used to maintain that layer relationship.


  • overlay-containers/ : Stores metadata for running and stopped containers, including configuration, writable layer references, mount information, and runtime state.
bash
ls -lash /var/lib/containers/storage/overlay-containers/

[root@Byte2Build ~]# ls -lash /var/lib/containers/storage/overlay-containers/
total 12K
   0 drwx------. 3 root root  124 Aug 13 06:27 .
4.0K drwx------. 8 root root 4.0K Aug 13 06:27 ..
   0 drwx------. 3 root root   22 Aug 13 06:27 2e7f71b5fcc832791a5859332881cfb2396d4966660b10fffd531a8227045f4e
4.0K -rw-------. 1 root root  601 Aug 13 06:27 containers.json
4.0K -rw-r--r--. 1 root root   64 Aug 13 06:27 containers.lock

Notice that this identifier is different from the Image ID: 39a151cad9e9f78ac5..   and  Container ID:  2e7f71b5fcc..

This gives us another fundamental concept: 

OUTPUT
            
          +-------+
          | IMAGE |
          +---+---+
              |
             run 
              |
              v
          +----------+
         | CONTAINER |
         +-----+-----+
              |
         +----+------+
         |           |
         v           v
   Image ID    Container ID

An image identifies the image. A container has its own container identity and state.

Therefore, multiple containers can be created from the same image while maintaining independent container state.


  • volumes/ : Stores persistent Podman volumes that remain available even after containers are removed.
bash
ls -lash /var/lib/containers/storage/volumes/

OUTPUT
[root@Byte2Build ~]#
 ls -lash /var/lib/containers/storage/volumes/
total 4.0K
   0 drwx------. 3 root root   78 Aug 13 06:27 .
4.0K drwx------. 8 root root 4.0K Aug 13 06:27 ..
   0 drwx------. 3 root root   19 Aug 13 06:27 280e750e21d88b44a15b88eee987dec25a361d1311695a93f7b106e4fb0f6ec1

This directory represents Podman-managed persistent volume storage.

A volume has a different purpose from the container's writable OverlayFS layer:

Container writable layer -> Container-specific runtime changes .

Volume -> Persistent application data.

For example, an application might write database data to a volume rather than relying on the container's writable layer.

Container lifecycle and persistent-data lifecycle are different. Removing a container does not necessarily mean the associated persistent volume must disappear.


  • libpod/: Stores Podman's internal database, which tracks images, containers, networks, and volumes.
bash
ls -lash /var/lib/containers/storage/libpod/

Depending on the Podman/storage version and configuration, you may see internal database/state files.

This is internal Podman state, rather than the actual filesystem contents of the image.



Byte2Build Podman image flow diagram showing Container Registry, OCI image layers, local container storage, OverlayFS, container root filesystem, OCI runtime, Linux kernel, PID 1, and running container.
Figure 1.4.4 - Podman Image Flow: From Registry to Container Run


The following diagram summarizes how the logical image hierarchy reported by podman image tree relates to Podman's local storage and how that storage eventually participates in creating the container root filesystem.