Docker: Images from scratch


When we started working for the first time with Docker and Docker containers, we tried to keep it simple and use the base images that are offered on Docker-Hub for free. These containers are really useful for testing environments, but it is risky to base your productive environment on them because, in the end, you do not really know what these images contain.

Another important subject is the operating system we want to use. A lot of distributions are not available on Docker-Hub, such as Red Hat Enterprise Linux. Those images are not available due to licensing needs. In this post, I will explain how to generate a base image (or bootstrap) of a Red Hat Enterprise Linux distribution.


In this example, we will create a base image container from a host with a distribution based on RHEL (CentOS) to simplify the generation process because we don’t need to install an extra tool to manage the RPM packages.

NOTE: If you are going to create the base image container from a host with a Debian-based distro, install and use rinse for managing RPM packages.

As previously mentioned, in this post I will generate a base image container of Red Hat Enterprise Linux. In order to do this, we will need to download the ISO from the Red Hat website ( Once we get the ISO image, (rhel-server-7.1-x86_64-dvd.iso, in this case) we will mount it in our host and we will create a local repository entry in the following path ‘/etc/yum.repos.d’:

In this example, the image is in the ‘/ tmp’ at my host

$ mkdir /mnt/rhel71_64

$ mount -o loop /tmp/rhel-server-7.1-x86_64-dvd.iso /mnt/rhel71_64

$ cat < “${TEMPORARY_PATH}”/etc/yum.repos.d/local.repo








Finally, we must have the Docker engine service on our host. You can install these services by clicking on the following link

First steps:

The first thing to do is to create the directory you will use as the root of the Guest OS that we want to install. We must execute the following commands:

$ cd /tmp

$ TARGET_PATH=”rhel7.1_64.XXXXXX”

$ TEMPORARY_PATH=$(mktemp -d –tmpdir ${TARGET_PATH})

NOTE: The mktemp command will dynamically generate a hash to avoid using the same directory twice that will make a root path of the Guest OS.

Now we will proceed to generate all the device files required for the Guest OS:

$ mkdir -m 755 “${TEMPORARY_PATH}”/dev

$ mknod -m 600 “${TEMPORARY_PATH}”/dev/console c 5 1

$ mknod -m 600 “${TEMPORARY_PATH}”/dev/initctl p

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/full c 1 7

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/null c 1 3

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/ptmx c 5 2

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/random c 1 8

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/tty c 5 0

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/tty0 c 4 0

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/urandom c 1 9

$ mknod -m 666 “${TEMPORARY_PATH}”/dev/zero c 1 5

NOTE: For references about the mknod command, please see the following link.

Guest OS Installation:

Once we have created the target directory and all devices files, we will proceed to install the Guest OS. In order to accomplish this step we need to run the yum command (remember that this is possible because our host is a distro-based on RHEL, otherwise use rinse or a similar command). Use the following parameters and indicate that you want to install the yum group core:

  • installroot=${TEMPORARY_PATH}:Directory which will act as the root for executing YUM.
  • releasever=/:Version / Release to be used during the execution of YUM.
  • setopt=tsflags=nodocs:Indicates that the documentation of the packages that will be installed (a.k.a. Man Pages) will be eliminated.
  • setopt=group_package_types=mandatory: Indicates that we will install only the packages required for the installation group that is being requested.
  • disablerepo=*:Disables all repositories to avoid sources of different packages that we want.
  • enablerepo=rhel71_64:Enables only the repository from which we will get the packages.

$ yum –installroot=${TEMPORARY_PATH} –releasever=/ –setopt=tsflags=nodocs –setopt=group_package_types=mandatory –disablerepo=* –enablerepo=rhel71_64 -y groupinstall core


Once we have already installed Guest OS, we need to make some minor modifications to avoid having problems after the operating system is packaged within an image.

– Network Config:

$ cat < “${TEMPORARY_PATH}”/etc/sysconfig/network




– Set SELinux to Permissive Mode

$ sed -i “s/SELINUX=[Ee]nforcing/SELINUX=permissive/g” “${TEMPORARY_PATH}”/etc/selinux/config

Extra Configuration (for Guest OS based on RHEL distribution only):

If you are installing Red Hat Enterprise Linux and you still do not have a license to use external repositories, you will have to create a local ‘yum’ repository on your LAN and set the Guest OS image, thus making extra packages available that will be  required for a future installation. In order to do this, once you have published the repository on your LAN, you can execute the following steps:

$ rm -rf “${TEMPORARY_PATH}”/etc/yum.repos.d/*

$ mkdir -p -m 755 “${TEMPORARY_PATH}”/etc/yum.repos.d

$ mkdir -p -m 755 “${TEMPORARY_PATH}”/etc/yum/vars

$ cat < “${TEMPORARY_PATH}”/etc/yum.repos.d/local.repo


name=”RedHat Local Repo”





$ echo “{{GUEST_OS_MAYOR_VERSION}}” > “${TEMPORARY_PATH}”/etc/yum/vars/releasever

NOTE: Replace {{GUEST_OS_MAYOR_VERSION}} with the version of Guest OS that we are installing. For this example, it will be 7.

Considerations Depending on Our Host OS:

If our host is based on a RHEL Distro 7 or higher and we are installing an older Guest OS (RHEL Distro 4/5/6), we must convert the RPM database since it was created with a newer version of yum than what the Guest OS has.

We will complete the next steps:

We will install packages capable of handling the yum database in Guest OS and remove the yum cache:

$ yum –installroot=${TEMPORARY_PATH} –releasever=/ –setopt=tsflags=nodocs –setopt=group_package_types=mandatory –disablerepo=* –enablerepo=rhel71_64 -y install yum compat-db db4-utils

$ rm -rf “${TEMPORARY_PATH}”/var/lib/rpm/__db*

Then, we will import the yum database with the following For Statement and rebuild the yum database:

$  for file in Packages Basenames Name Providename Conflictname; do

$      mv -v ${TEMPORARY_PATH}/var/lib/rpm/${file} ${TEMPORARY_PATH}/var/lib/rpm/${file}.original

$      db_dump ${TEMPORARY_PATH}/var/lib/rpm/${file}.original | chroot “${TEMPORARY_PATH}” /usr/bin/db_load /var/lib/rpm/${file}

$      rm -rf ${TEMPORARY_PATH}/var/lib/rpm/${file}.original

$  done

$  chroot “${TEMPORARY_PATH}” /bin/rpm –rebuilddb

Let’s Clean Our Playground:

Once we have installed everything we need in the base image, we have to clean everything that could occupy space within the image that will not be necessary, or that is dynamically generated when we instantiate the image. Every byte counts!

$ rm -rf “${TEMPORARY_PATH}”/usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive}

$ rm -rf “${TEMPORARY_PATH}”/usr/share/{man,doc,info,gnome/help}

$ rm -rf “${TEMPORARY_PATH}”/usr/share/cracklib

$ rm -rf “${TEMPORARY_PATH}”/usr/share/i18n

$ rm -rf “${TEMPORARY_PATH}”/sbin/sln

$ rm -rf “${TEMPORARY_PATH}”/etc/

$ rm -rf “${TEMPORARY_PATH}”/var/cache/ldconfig/*

$ mkdir -p -m 755 “${TEMPORARY_PATH}”/usr/share/info

Docker Time!

By now, we should have everything we need to generate the image, so we just need to import the content of the root path within a Docker image:

$ tar –numeric-owner -c -C “${TEMPORARY_PATH}” . | sudo docker import – rhel:7.1

Once we have created the image, we clean the remnant:


That’s All Folks

You may also like


Kubernetes Vs. Docker 


Containerizing Apps, not VMs

developer's life intraway

A Day in a Developer’s Life at Intraway