Objective: Design custom container images following good practice, and build them using Dockerfiles/Containerfiles.
Overview
Pre-built images cover common services, but real applications usually need a custom image that layers application code and configuration onto a suitable base. Designing that image well, and building it in a repeatable way, is a core skill for anyone shipping containerized software.
Key Concepts
Design principles for custom images
Well-designed images are small, single-purpose, and predictable: one main process per container, no unnecessary packages, no secrets baked into layers, and configuration supplied at runtime (environment variables, mounted config, or command arguments) rather than hardcoded.
Choosing a base image
Custom images almost always start FROM an existing base — a minimal RHEL UBI (Universal Base Image) for general-purpose builds, or a language-specific base (for example a Node.js or Python UBI image) when the application's runtime dependencies are already well understood.
Dockerfile / Containerfile instructions
A build file is a sequence of instructions executed in order; Podman calls the file a Containerfile (identical syntax to a Dockerfile).
| Instruction | Purpose |
|---|---|
FROM |
Selects the base image the new image is built on top of |
RUN |
Executes a command at build time, committing the result as a new layer |
COPY / ADD |
Copies files from the build context into the image |
ENV |
Sets an environment variable available at build and run time |
EXPOSE |
Documents which port(s) the container listens on |
USER |
Sets the user the container process runs as |
CMD |
Default command run when the container starts (can be overridden) |
ENTRYPOINT |
The fixed executable a container always runs; CMD supplies default arguments to it |
Building an image
podman build reads a Containerfile and a build context (the directory of files available to COPY/ADD), executing each instruction to produce a final, tagged image.
# Containerfile
FROM registry.access.redhat.com/ubi8/ubi-minimal
RUN microdnf install -y httpd && microdnf clean all
COPY ./site/ /var/www/html/
EXPOSE 8080
USER 1001
CMD ["httpd", "-D", "FOREGROUND"]
Layer order and build cache
Podman caches each layer and reuses it on subsequent builds if nothing that produced it has changed. Ordering instructions from least-likely-to-change (installing packages) to most-likely-to-change (copying application source) keeps rebuilds fast, since only the layers after the first change need to be rebuilt.
Working with It
Podman reads the exact same syntax as Docker; the file is usually still named Dockerfile, or Containerfile if you want the vendor-neutral name explicitly.
Put steps that change rarely (installing OS packages) before steps that change often (copying your application code) so a code change doesn't invalidate the expensive package-installation layer.
Unless the application genuinely requires root, set a non-root USER in the image — running as root inside the container is one of the most common avoidable security mistakes in custom images.
Building and running a custom image
$ podman build -t quay.io/myorg/mysite:1.0 .
$ podman run -d --name mysite -p 8080:8080 quay.io/myorg/mysite:1.0
Watch Out For
Unless the application genuinely requires root, set a non-root USER in the image — running as root inside the container is one of the most common avoidable security mistakes in custom images.
Key Terms
- Containerfile
- A build instruction file (Podman's name for a Dockerfile).
- Base image
- The starting image a custom image is built FROM.
- Build context
- The set of local files available to COPY/ADD during a build.
- CMD vs. ENTRYPOINT
- CMD supplies default (overridable) arguments; ENTRYPOINT is the fixed command that runs.
- Build cache
- Reuse of previously-built, unchanged layers to speed up subsequent builds.
Review Questions
- What is the difference between CMD and ENTRYPOINT?
- Why should the instruction that installs OS packages usually appear before the instruction that copies application code?
- What security practice should you apply to the USER instruction, and why?
- What is the build context, and what command supplies it?
- Name two properties of a well-designed container image besides being small.