Building Images
A container image is built from a set of instructions written in a text file. That file is called a Dockerfile. Despite the name, Podman uses the exact same Dockerfile format. You do not need Docker installed.
Think of a Dockerfile as a recipe. Each instruction adds a layer to your image. The final result is a portable snapshot that anyone can run.
A minimal Dockerfile
Section titled “A minimal Dockerfile”Create a new directory for this example. Then create a file named Dockerfile inside it.
$ mkdir samtools-image$ cd samtools-imageWrite the following into Dockerfile:
# Start from Ubuntu 24.04FROM ubuntu:24.04
# Install samtoolsRUN apt-get update && \ apt-get install -y samtools && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*
# Set the working directoryWORKDIR /data
# Default commandCMD ["samtools", "--version"]Let’s walk through each line.
FROM ubuntu:24.04sets the base image. Every Dockerfile starts withFROM. This pulls Ubuntu 24.04 as the foundation.RUN apt-get update && ...executes commands during the build. Here it installs samtools. The&&chains keep everything in a single layer.WORKDIR /datasets/dataas the working directory. Any subsequent commands will run from this location.CMD ["samtools", "--version"]defines the default command. When someone runs this image without specifying a command, it will print the samtools version.
Common Dockerfile instructions
Section titled “Common Dockerfile instructions”Every Dockerfile begins with FROM. It specifies which base image to start from.
FROM ubuntu:24.04Always use a specific tag like 24.04 or 22.04. Never use latest. The latest tag can change without warning and break your builds. Pinning a version keeps your image reproducible.
RUN executes a command during the build process. It is most commonly used to install software.
RUN apt-get update && \ apt-get install -y samtools bwa fastqc && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*Chain related commands with && on a single RUN line. Each RUN creates a new image layer. Fewer layers mean smaller images.
COPY transfers files from your local machine into the image.
COPY run_analysis.sh /opt/scripts/run_analysis.shThe first argument is the local path relative to the build context. The second argument is the destination inside the image.
WORKDIR
Section titled “WORKDIR”WORKDIR sets the working directory for all instructions that follow.
WORKDIR /dataIf the directory does not exist, it will be created automatically. This is cleaner than using RUN mkdir /data && cd /data.
ENV sets environment variables that persist when the container runs.
ENV PATH="/opt/tools/bin:${PATH}"ENV THREADS=4This is useful for adding custom tools to the PATH or setting default parameters.
CMD specifies the default command that runs when a container starts. Each Dockerfile should have exactly one CMD.
CMD ["fastqc", "--help"]Users can override this by passing a different command at runtime.
Building the image
Section titled “Building the image”From the directory containing your Dockerfile, run:
$ podman build -t samtools-image .-t samtools-imageassigns a name and tag to the image. Without-t, you would have to reference the image by its ID..is the build context. It tells Podman to look for the Dockerfile in the current directory. AllCOPYpaths are relative to this location.
The build will download the Ubuntu base image, run the installation commands, and produce a final image. You will see output for each step.
$ podman build -t samtools-image .STEP 1/4: FROM ubuntu:24.04STEP 2/4: RUN apt-get update && ...STEP 3/4: WORKDIR /dataSTEP 4/4: CMD ["samtools", "--version"]Successfully tagged localhost/samtools-image:latestA bioinformatics example: R with DESeq2
Section titled “A bioinformatics example: R with DESeq2”Suppose you want a portable environment for differential expression analysis. You need R, BiocManager, and DESeq2. You also want to include your own analysis script.
First, create a simple R script named run_deseq2.R:
library(DESeq2)
counts <- read.csv("/data/counts.csv", row.names = 1)coldata <- read.csv("/data/coldata.csv", row.names = 1)
dds <- DESeqDataSetFromMatrix( countData = counts, colData = coldata, design = ~ condition)
dds <- DESeq(dds)res <- results(dds)write.csv(as.data.frame(res), "/data/results.csv")message("DESeq2 analysis complete.")Now write the Dockerfile:
FROM rocker/r-ver:4.4.0
# Install system dependencies for R packagesRUN apt-get update && \ apt-get install -y \ libcurl4-openssl-dev \ libxml2-dev \ libssl-dev && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*
# Install BiocManager and DESeq2RUN R -e "install.packages('BiocManager', repos='https://cloud.r-project.org')" && \ R -e "BiocManager::install('DESeq2', ask=FALSE)"
# Copy the analysis scriptCOPY run_deseq2.R /opt/scripts/run_deseq2.R
# Set working directory for dataWORKDIR /data
# Run the script by defaultCMD ["Rscript", "/opt/scripts/run_deseq2.R"]Build it:
$ podman build -t deseq2-image .This build will take several minutes. R packages and their dependencies are large. Once complete, anyone with this image can run your exact DESeq2 analysis without installing anything.
Layer caching
Section titled “Layer caching”Podman caches each layer of a Dockerfile. If a layer has not changed, Podman reuses the cached version instead of rebuilding it. This speeds up repeated builds significantly.
The key rule: put things that change rarely at the top. Put things that change often at the bottom.
System packages rarely change. Your analysis scripts change frequently. Structure your Dockerfile accordingly.
FROM ubuntu:24.04
# Rarely changes: install system packages firstRUN apt-get update && \ apt-get install -y samtools bwa && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*
# Changes often: copy your script lastCOPY pipeline.sh /opt/scripts/pipeline.sh
CMD ["bash", "/opt/scripts/pipeline.sh"]If you edit pipeline.sh and rebuild, Podman will skip the package installation step entirely. It will only rerun the COPY and everything after it. This can save minutes on each rebuild.
Reducing image size
Section titled “Reducing image size”Container images can grow large quickly. A few practices help keep them small.
Clean up after installing packages
Section titled “Clean up after installing packages”Always remove package caches in the same RUN command as the installation. If you put the cleanup in a separate RUN, the cache files still exist in a previous layer.
# Good: clean up in the same RUN commandRUN apt-get update && \ apt-get install -y samtools && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*
# Bad: cleanup in a separate layer (files still exist in the previous layer)RUN apt-get update && apt-get install -y samtoolsRUN apt-get cleanCombine related RUN commands
Section titled “Combine related RUN commands”Each RUN instruction adds a layer. Combine related installation steps into a single RUN to minimize layers.
# Better: one layerRUN apt-get update && \ apt-get install -y samtools bwa fastqc && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*
# Worse: three layersRUN apt-get update && apt-get install -y samtoolsRUN apt-get install -y bwaRUN apt-get install -y fastqcUse smaller base images
Section titled “Use smaller base images”When possible, use minimal base images. Alpine Linux images are much smaller than Ubuntu. However, some bioinformatics tools have dependencies that make Alpine impractical. Choose the smallest base that works for your tools.
Listing and removing images
Section titled “Listing and removing images”To see all images on your system:
$ podman imagesREPOSITORY TAG IMAGE ID SIZElocalhost/deseq2-image latest a1b2c3d4e5f6 1.82 GBlocalhost/samtools-image latest f6e5d4c3b2a1 145 MBdocker.io/library/ubuntu 24.04 1234abcd5678 78.1 MBTo remove an image you no longer need:
$ podman rmi samtools-imageTo remove all unused images at once:
$ podman image prune -aBe careful with prune -a. It removes every image that is not currently used by a running container.
Quick reference
Section titled “Quick reference”| Instruction | Purpose | Example |
|---|---|---|
FROM |
Set base image | FROM ubuntu:24.04 |
RUN |
Run a command during build | RUN apt-get install -y samtools |
COPY |
Copy files into image | COPY script.sh /opt/script.sh |
WORKDIR |
Set working directory | WORKDIR /data |
ENV |
Set environment variable | ENV THREADS=4 |
CMD |
Default run command | CMD ["samtools", "--version"] |
podman build -t name . |
Build an image | podman build -t my-image . |
podman images |
List all images | |
podman rmi name |
Remove an image | podman rmi my-image |
Next steps
Section titled “Next steps”Now that you can build images, the next page covers Running Containers and working with them interactively.