Podman can build OCI images completely independently of Docker, and it’s often faster because it doesn’t need a daemon running.
Let’s see Podman build an image for a simple Python web server.
First, we need a Dockerfile.
FROM registry.access.redhat.com/ubi8/python-38
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["python", "app.py"]
And the application files:
requirements.txt:
flask
app.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello from Podman!'
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8080)
Now, let’s build this with Podman. We’ll create a directory named myapp and put these three files inside it.
$ podman build -t my-python-app:latest ./myapp
The output will look something like this, showing each build step:
STEP 1: FROM registry.access.redhat.com/ubi8/python-38
--> 5e4027640517
STEP 2: WORKDIR /app
--> 6f173d2b4629
STEP 3: COPY requirements.txt .
--> 1234567890ab
STEP 4: RUN pip install --no-cache-dir -r requirements.txt
--> aabbccddeeff
STEP 5: COPY . .
--> fedcba098765
STEP 6: EXPOSE 8080
--> 1a2b3c4d5e6f
STEP 7: CMD ["python", "app.py"]
--> 67890abcdef0
Successfully tagged my-python-app:latest
You can see that Podman executes each instruction in the Dockerfile. It pulls the base image, sets the working directory, copies the requirements.txt, installs the Python dependencies, copies the application code, exposes the port, and sets the default command.
To run this image, you’d use podman run:
$ podman run -d -p 8080:8080 my-python-app:latest
And then you can access http://localhost:8080 in your browser, and you’ll see "Hello from Podman!".
The core of Podman’s image building is its ability to interpret Dockerfiles and execute the instructions using Buildah under the hood. Buildah is a tool designed specifically for building OCI-compliant container images, and it can do so without requiring a persistent daemon. This "daemonless" architecture is a key differentiator from Docker. When you run podman build, it invokes Buildah to create a working container, execute the build commands within that container, and then commit the resulting filesystem as a new image. This process is often more efficient as it avoids the overhead of inter-process communication with a central daemon.
Podman also supports Containerfiles, which are essentially the same as Dockerfiles but are the upstream OCI standard naming convention. You can use podman build -f Containerfile . interchangeably with podman build -f Dockerfile ..
One of the subtle but powerful aspects of Podman’s build process is its handling of build cache. Like Docker, Podman caches the results of each build step. If you modify a file that’s copied after a certain build step, only the subsequent steps will be re-executed. However, Podman’s cache management can sometimes be more transparently controlled, especially when dealing with external dependencies or complex build environments. For instance, if a RUN command fetches external data, Podman’s Buildah backend doesn’t inherently invalidate the cache for that step unless the command itself changes or dependencies are explicitly re-fetched. This means you might need to be mindful of how RUN commands interact with external services if you’re not seeing expected cache misses.
The next concept you’ll likely explore is how to optimize these builds for smaller image sizes and faster execution, often involving multi-stage builds.