Docker Optimization
RHEL
CentOS
Fedora
Image Size Reduction

How to reduce the size of RHEL/Centos/Fedora Docker image

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

RHEL, CentOS, and Fedora images can become large quickly because package managers, metadata, docs, and build tooling all add layers and files. The most effective way to reduce size is to combine a smaller base image with disciplined package installation and multi-stage builds. The goal is not just a smaller download, but a cleaner runtime image with fewer unnecessary components.

Start From the Smallest Reasonable Base

The easiest size win is choosing the right base image. If you only need a minimal runtime, use one of the reduced variants instead of a full distribution image.

Typical options include:

  • 'registry.access.redhat.com/ubi9/ubi-minimal'
  • 'registry.access.redhat.com/ubi9/ubi-micro'
  • 'fedora-minimal'

For example:

dockerfile
1FROM registry.access.redhat.com/ubi9/ubi-minimal
2
3RUN microdnf install -y python3 \
4    && microdnf clean all
5
6CMD ["python3", "--version"]

Using a smaller base matters more than almost any later micro-optimization.

Install Only What the Runtime Actually Needs

A common mistake is leaving compilers, development headers, and debugging tools in the final image. If the container only runs an already-built app, the runtime image should usually contain just:

  • the app binary or source needed at runtime
  • the runtime libraries
  • configuration or certificates that the app actually uses

Avoid installing package groups or generic utility bundles unless they are genuinely required.

Clean Package Metadata in the Same Layer

Package managers leave caches and metadata behind unless you remove them. On minimal Fedora or UBI images, that usually means cleaning in the same RUN instruction.

dockerfile
1FROM registry.access.redhat.com/ubi9/ubi-minimal
2
3RUN microdnf install -y curl ca-certificates \
4    && microdnf clean all \
5    && rm -rf /var/cache/yum /var/cache/dnf

Doing the cleanup in a later layer does not help much, because the earlier layer still contains the original files.

Use Multi-Stage Builds

If you compile code inside the image, build it in one stage and copy only the result into a smaller runtime stage.

dockerfile
1FROM registry.fedoraproject.org/fedora:40 AS build
2
3RUN dnf install -y gcc make
4WORKDIR /src
5COPY hello.c .
6RUN gcc -O2 -o hello hello.c
7
8FROM registry.access.redhat.com/ubi9/ubi-micro
9COPY --from=build /src/hello /usr/local/bin/hello
10
11ENTRYPOINT ["/usr/local/bin/hello"]

This pattern avoids shipping the compiler, package manager metadata, and source tree in the final image.

Every RUN instruction creates a layer. Combining related package installation and cleanup steps reduces wasted layers.

dockerfile
RUN microdnf install -y tar gzip \
    && microdnf clean all \
    && rm -rf /tmp/*

That said, readability still matters. Do not collapse unrelated logic into one unreadable line just to save a tiny amount of space.

Avoid Copying More Than Necessary

Large build contexts often bloat images more than the base OS does. Use a .dockerignore file to exclude things such as:

  • '.git'
  • local caches
  • test data
  • node_modules created on the host
  • build artifacts that will be regenerated

Example .dockerignore:

text
1.git
2__pycache__
3node_modules
4dist
5build
6*.log

This reduces both build time and image size.

Consider UBI Micro for Strict Runtime Images

If you do not need a shell or package manager in the final container, ubi-micro can be a strong option. It is much smaller, but that also means debugging inside the container becomes harder. That tradeoff is often worth it for production runtime images where repeatable builds matter more than interactive troubleshooting.

Measure the Result

Optimization should be verified, not guessed. Useful commands include:

bash
docker images
docker history your-image:latest

docker history helps identify unexpectedly large layers. It often reveals that one COPY or one package install step dominates the final size.

Common Pitfalls

The biggest mistake is starting from a full distribution image when a minimal variant would work. Another is installing build dependencies into the final runtime stage. Developers also clean package caches in a later layer, which does not remove the bytes from earlier layers. Copying the entire repository into the image without a .dockerignore file is another common source of bloat. Finally, shrinking the image too aggressively can remove tools that the app truly needs, so size reduction still has to respect runtime correctness.

Summary

  • Pick a minimal base image such as ubi-minimal, ubi-micro, or fedora-minimal when possible.
  • Install only the packages needed at runtime.
  • Clean package metadata in the same RUN layer as installation.
  • Use multi-stage builds to keep compilers and build tools out of the final image.
  • Keep the build context small with a good .dockerignore.
  • Measure size changes with docker images and docker history.

Course illustration
Course illustration

All Rights Reserved.