Red Hat Training

A Red Hat training course is available for Red Hat Enterprise Linux

Chapter 1. Finding, Running, and Building Containers with podman, skopeo, and buildah

1.1. Overview

Red Hat Enterprise Linux offers a set of container tools to work directly with Linux containers and container images that requires no container engine or docker commands or services. These tools include:

  • podman: The podman command can run and manage containers and container images. It supports the same features and command options you find in the docker command, with the main differences being that podman doesn’t require the docker service or any other active container engine for the command to work. Also, podman stores its data in the same directory structure used by Buildah, Skopeo, and CRI-O, which will allow podman to eventually work with containers being actively managed by CRI-O in OpenShift.

    Podman has a lot of advanced features, such as the support for running containers in Pods. It fully integrates with systemd, including the ability to generate unit files from containers and run systemd within a container. Podman also offers User Namespace support, including running containers without requiring root.

  • skopeo: The skopeo command is a tool for copying containers and images between different types of container storage. It can copy containers from one container registry to another. It can copy images to and from a host, as well as to other container environments and registries. Skopeo can inspect images from container image registries, get images and image layers, and use signatures to create and verify images.
  • buildah: The buildah command allows you to build container images either from command line or using Dockerfiles. These images can then be pushed to any container registry and can be used by any container engine, including Podman, CRI-O, and Docker. The buildah command can be used as a separate command, but is incorporated into other tools as well. For example the podman build command used buildah code to build container images. Buildah is also often used to securely build containers while running inside of a locked down container by a tool like Podman, OpenShift/Kubernetes or Docker.
  • OCI Runtimes:

    • runc: The runc command can be used to start up OCI containers.

The following sections describe how to set up and use podman, runc, skopeo, and buildah.

1.2. Running containers as root or rootless

Running the container tools described in this chapter as a user with superuser privilege (root user) is the best way to ensure that your containers have full access to any feature available on your system. However, with a new feature called "Rootless Containers," available now as a Technology Preview, you can work with containers as a regular user.

Although container engines, such as Docker, let you run docker commands as a regular (non-root) user, the docker daemon that carries out those requests runs as root. So, effectively, regular users can make requests through their containers that harm the system, without there being clarity about who made those requests. By setting up rootless container users, system administrators limit potentially damaging container activities from regular users, while still allowing those users to safely run many container features under their own accounts.

This section describes how to set up your system to use container tools (Podman, Skopeo, and Buildah) to work with containers as a non-root user (rootless). It also describes some of the limitations you will encounter because regular user accounts don’t have full access to all operating system features that their containers might need to run.

1.2.1. Set up for rootless containers

You need to become root user to set up your RHEL system to allow non-root user accounts to use container tools such as podman, skopeo, and buildah, as following:

  1. Install RHEL 7.7: Install or upgrade to RHEL 7.7. Earlier RHEL 7 versions are missing features needed for this procedure. If you are upgrading to RHEL 7.7, continue to "Upgrade to rootless containers" after this procedure is done.
  2. Install slirp4netns: Install the slirp4netns package (and also podman, just to get you started):

    # yum install slirp4netns podman -y
  3. Increase user namespaces: To increase the number of user namespaces in the kernel, type the following:

    # echo "user.max_user_namespaces=28633" > /etc/sysctl.d/userns.conf
    # sysctl -p /etc/sysctl.d/userns.conf
  4. Create the new user account: To create a new user account and add a password for that account (for example, joe), type the following:

    # useradd -c "Joe Jones" joe
    # passwd joe

    The user is automatically configured to be able to use rootless podman.

  5. Try a podman command: Log in directly as the user you just configured (don’t use su or su - to become that user because that doesn’t set the correct environment variables) and try to pull and run an image:

    # podman pull ubi7/ubi
    # podman run ubi7/ubi cat /etc/os-release
    NAME="Red Hat Enterprise Linux Server"
    VERSION="7.7 (Maipo)"
    ...
  6. Check rootless configuration: To check that your rootless configuration is set up properly, you can run commands inside the modified user namespace with the podman unshare command. As the rootless user, the following command lets you see how the uids are assigned to the user namespace:

    $ podman unshare cat /proc/self/uid_map
             0       1001     1
             1       100000   65536
         65537       165536   65536

1.2.2. Upgrade to rootless containers

If you have upgraded from RHEL 7.6, you must configure subuid and subgid values manually for any existing user you want to be able to use rootless podman.

Using an existing user name and group name (for example, jill), set the range of accessible user and group IDs that can be used for their containers. Here are a couple of warnings:

  • Don’t include the rootless user’s UID and GID in these ranges
  • If you set multiple rootless container users, use unique ranges for each user
  • We recommend 65536 UIDs and GIDs for maximum compatibility with existing container images, but the number can be reduced
  • Never use UIDs or GIDs under 1000 or reuse UIDs or GIDs from existing user accounts (which, by default, start at 1000)

Here is an example:

# echo "jill:200000:65536" >> /etc/subuid
# echo "jill:200000:65536" >> /etc/subgid

The user/group jill is now allocated 65535 user and group IDs, ranging from 200000-265536. That user should be able to begin running commands to work with containers now.

1.2.3. Special considerations for rootless

Here are some things to consider when running containers as a non-root user:

  • As a non-root container user, container images are stored under your home directory ($HOME/.local/share/containers/storage/), instead of /var/lib/containers.
  • Users running rootless containers are given special permission to run as a range of user and group IDs on the host system. However, they otherwise have no root privileges to the operating system on the host.
  • If you need to configure your rootless container environment, edit configuration files in your home directory ($HOME/.config/containers). Configuration files include storage.conf (for configuring storage) and libpod.conf (for a variety of container settings). You could also create a registries.conf file to identify container registries available when you run podman pull or podman run.
  • For RHEL 7, rootless containers are limited to VFS storage. VFS storage does not support deduplication. So, for example, if you have a 1GB image, then starting a container will result in copying that 1GB again for the container. Starting another container from that image will result in another 1GB of space being used. This limitation is planned to be addressed in future releases by backporting fuse-overlay to the RHEL 7 kernel.
  • A container running as root in a rootless account can turn on privileged features within its own namespace. But that doesn’t provide any special privileges to access protected features on the host (beyond having extra UIDs and GIDs). Here are examples of container actions you might expect to work from a rootless account that will not work:

    • Anything you want to access from a mounted directory from the host must be accessible by the UID running your container or your request to access that component will fail.
    • There are some system features you won’t be able to change without privilege. For example, you cannot change the system clock by simply setting a SYS_TIME capability inside a container and running the network time service (ntpd). You would have to run that container as root, bypassing your rootless container environment and using the root user’s environment, for that capability to work, such as:

      $ sudo podman run -d --cap-add SYS_TIME ntpd

      Note that this example allows ntpd to adjust time for the entire system, and not just within the container.

  • A rootless container has no ability to access a port less than 1024. Inside the rootless container’s namespace it can, for example, start a service that exposes port 80 from an httpd service from the container:

    $ podman run -d httpd

    However, a container would need root privilege, again using the root user’s container environment, to expose that port to the host system:

    $ sudo podman run -d -p 80:80 httpd
  • An on-going list of shortcomings of running podman and related tools without root privilege is contained in Shortcomings of Rootless Podman.

1.3. Using podman to work with containers

The podman command lets you run containers as standalone entities, without requiring that Kubernetes, the Docker runtime, or any other container runtime be involved. It is a tool that can act as a replacement for the docker command, implementing the same command-line syntax, while it adds even more container management features. The podman features include:

  • Based on docker interface: Because podman syntax mirrors the docker command, transitioning to podman should be easy for those familiar with docker.
  • Managing containers and images: Both Docker- and OCI-compatible container images can be used with podman to:

    • Run, stop and restart containers
    • Create and manage container images (push, commit, configure, build, and so on)
  • Working with no runtime: No runtime environment is used by podman to work with containers.

Here are a few implementation features of podman you should know about:

  • Podman uses the CRI-O back-end store directory, /var/lib/containers, instead of using the Docker storage location (/var/lib/docker), by default.
  • Although podman and CRI-O share the same storage directory, they cannot interact with each other’s containers. (Eventually the two features will be able to share containers.)
  • The podman command, like the docker command, can build container images from a Dockerfile.
  • The podman command can be a useful troubleshooting tool when the docker service is unavailable.
  • Options to the docker command that are not supported by podman include container, events, image, network, node, plugin (podman does not support plugins), port, rename (use rm and create to rename container with podman), secret, service, stack, swarm (podman does not support Docker Swarm), system, and volume (for podman, create volumes on the host, then mount in a container). The container and image options are used to run subcommands that are used directly in podman.
  • The following features are currently in development for podman:

    • To interact programmatically with podman, a remote API for Podman is being developed using a technology called varlink. This will let podman listen for API requests from remote tools (such as Cockpit or the atomic command) and respond to them.
    • A feature in development will allow podman to run and manage a Pod (which may consist of multiple containers and some metadata) without Kubernetes or OpenShift being active. (However, podman is not expected to do some of Kubernetes’ more advanced features, such as scheduling pods across clusters).
Note

The podman command is considered to be technology preview for RHEL and RHEL Atomic 7.5.1.

1.3.1. Installing podman

To start using podman to work with containers, you can simply install it on a Red Hat Enterprise Linux server system or try it on a RHEL Atomic Host (podman is preinstalled on RHEL Atomic Host 7.5.1 or later). No container runtime is needed to use podman.

To install podman on a RHEL server system, do the following:

# subscription-manager repos --disable=’*’
# subscription-manager repos --enable=rhel-7-server-rpms
# subscription-manager repos --enable=rhel-7-server-extras-rpms
# subscription-manager repos --enable=rhel-7-server-optional-rpms
# yum install podman -y

1.3.2. Running containers with podman

If you are used to using the docker command to work with containers, you will find most of the features and options match those of podman. Table 1 shows a list of commands you can use with podman (type podman -h to see this list):

Table 1.1. Commands supported by podman

podman command

Description

podman command

Description

attach

Attach to a running container

commit

Create new image from changed container

build

Build an image using Dockerfile instructions

create

Create, but do not start, a container

diff

Inspect changes on container’s filesystems

exec

Run a process in a running container

export

Export container’s filesystem contents as a tar archive

help, h

Shows a list of commands or help for one command

history

Show history of a specified image

images

List images in local storage

import

Import a tarball to create a filesystem image

info

Display system information

inspect

Display the configuration of a container or image

kill

Send a specific signal to one or more running containers

load

Load an image from an archive

login

Login to a container registry

logout

Logout of a container registry

logs

Fetch the logs of a container

mount

Mount a working container’s root filesystem

pause

Pauses all the processes in one or more containers

ps

List containers

port

List port mappings or a specific mapping for the container

pull

Pull an image from a registry

push

Push an image to a specified destination

restart

Restart one or more containers

rm

Remove one or more containers from host. Add -f if running.

rmi

removes one or more images from local storage

run

run a command in a new container

save

Save image to an archive

search

search registry for image

start

Start one or more containers

stats

Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers

stop

Stop one or more containers

tag

Add an additional name to a local image

top

Display the running processes of a container

umount, unmount

Unmount a working container’s root filesystem

unpause

Unpause the processes in one or more containers

version

Display podman version information

1.3.3. Trying basic podman commands

Because the use of podman mirrors the features and syntax of the docker command, you can refer to Working with Docker Formatted Container Images for examples of how to use those options to work with containers. Simply replace docker with podman in most cases. Here are some examples of using podman.

1.3.3.1. Pull a container image to the local system

# podman pull registry.access.redhat.com/rhel7/rhel
Trying to pull registry.access.redhat...Getting image source signatures
Copying blob sha256:d1fe25896eb5cbcee...
Writing manifest to image destination
Storing signatures
fd1ba0b398a82d56900bb798c...

1.3.3.2. List local container images

# podman images
REPOSITORY                                     TAG      IMAGE ID       CREATED       SIZE
registry.access.redhat.com/rhel7/rhel-minimal  latest   de9c26f23799   5 weeks ago   80.1MB
registry.access.redhat.com/rhel7/rhel          latest   fd1ba0b398a8   5 weeks ago   211MB

1.3.3.3. Run a container image

This runs a container image and opens a shell inside the container:

# podman run -it registry.access.redhat.com/rhel7/rhel /bin/bash
[root@8414218c04f9 /]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 13:48 pts/0    00:00:00 /bin/bash
root        21     1  0 13:49 pts/0    00:00:00 ps -ef
[root@8414218c04f9 /]# exit
#

1.3.3.4. List containers that are running or have exited

# podman ps -a
CONTAINER ID   IMAGE                                             COMMAND
   CREATED AT                      STATUS                  PORTS NAMES
440becd26893   registry.access.redhat.com/rhel7/rhel-minimal:latest  /bin/bash
   2018-05-10 09:02:52 -0400 EDT   Exited (0) About an hour ago  happy_hodgkin
8414218c04f9   registry.access.redhat.com/rhel7/rhel:latest          /bin/bash
   2018-05-10 09:48:07 -0400 EDT   Exited (0) 14 minutes ago     nostalgic_boyd

1.3.3.5. Remove a container or image

Remove a container by its container ID:

# podman rm 440becd26893

1.3.3.6. Remove a container image by its image ID or name (use -f to force):

# podman rmi registry.access.redhat.com/rhel7/rhel-minimal
# podman rmi de9c26f23799
# podman rmi -f registry.access.redhat.com/rhel7/rhel:latest

1.3.3.7. Build a container

# cat Dockerfile
FROM registry.access.redhat.com/rhel7/rhel-minimal
ENTRYPOINT "echo "Podman build this container."

# podman build -t podbuilt .
STEP 1: FROM registry.access...
...
Writing manifest to image destination
Storing signatures
91e043c11617c08d4f8...

# podman run podbuilt
Podman build this container.

1.4. Running containers with runc

"runC" is a lightweight, portable implementation of the Open Container Initiative (OCI) container runtime specification. runC unites a lot of the low-level features that make running containers possible. It shares a lot of low-level code with Docker but it is not dependent on any of the components of the Docker platform. It supports Linux namespaces, live migration, and has portable performance profiles. It also provides full support for Linux security features such as SELinux, control groups (cgroups), seccomp, and others. You can build and run images with runc, or you can run docker-formatted images with runc.

1.4.1. Installing and running containers

The runc package is available for Red Hat Enterprise Linux in the Extras channel. You need to have the Extras channel enabled to install it with yum. If you are using Red Hat Enterprise Linux Atomic Host, the runc package is already included. For a regular RHEL system, to enable the extras repository and install the package, run:

$ sudo subscription-manager repos --enable=rhel-7-server-extras-rpms
$ sudo yum install runc

With runc, containers are configured using bundles. A bundle for a container is a directory that includes a specification file named "config.json" and a root filesystem. The root filesystem contains the contents of the container.

To create a bundle:

$ runc spec

This command creates a config.json file that only contains a bare-bones structure that you will need to edit. Most importantly, you will need to change the "args" parameter to identify the executable to run. By default, "args" is set to "sh".

    "args": [
      "sh"
    ],

As an example, you can download the docker-formatted Red Hat Enterprise Linux base image (rhel/rhel7) using docker, then export it, create a new bundle for it with runc, and edit the "config.json" file to point to that image. You can then create the container image and run an instance of that image with runc. Use the following commands:

$ sudo docker pull registry.access.redhat.com/rhel7/rhel
$ sudo docker export $(docker create registry.access.redhat.com/rhel7/rhel) > rhel.tar
$ mkdir -p rhel-runc/rootfs
$ tar -C rhel-runc/rootfs -xf rhel.tar
$ runc spec -b rhel-runc
$ vi rhel-runc/config.json      Change the value of terminal from *false* to *true*
$ sudo runc create -b rhel-runc/ rhel-container
$ sudo runc start rhel-container
sh-4.2#

In this example, the name of the container instance is "rhel-container". Running that container, by default, starts a shell, so you can begin looking around and running commands from inside that container. Type exit when you are done.

The name of a container instance must be unique on the host. To start a new instance of a container:

# runc start <container_name>

You can provide the bundle directory using the "-b" option. By default, the value for the bundle is the current directory.

You will need root privileges to start containers with runc. To see all commands available to runc and their usage, run "runc --help".

1.5. Using skopeo to work with container registries

With the skopeo command, you can work with container images from registries without using the docker daemon or the docker command. Registries can include the Docker Registry, your own local registries, or Atomic registries. Activities you can do with skopeo include:

  • inspect: The output of a skopeo inspect command is similar to what you see from a docker inspect command: low-level information about the container image. That output can be in json format (default) or raw format (using the --raw option).
  • copy: With skopeo copy you can copy a container image from a registry to another registry or to a local directory.
  • layers: The skopeo layers command lets you download the layers associated with images so that they are stored as tarballs and associated manifest files in a local directory.

Like the buildah command and other tools that rely on the containers/image library, the skopeo command can work with images from container storage areas other than those associated with Docker. Available transports to other types of container storage include: containers-storage (for images stored by buildah and CRI-O), ostree (for atomic and system containers), oci (for content stored in an OCI-compliant directory), and others. See the skopeo man page for details.

To try out skopeo, you could set up a local registry, then run the commands that follow to inspect, copy, and download image layers. If you want to follow along with the examples, start by doing the following:

  • Install a local registry as described in Working with Docker Registries.
  • Pull the latest RHEL 7 image to your local system (docker pull rhel7/rhel).
  • Retag the RHEL 7 image and push it to your local registry as follows:

    $ sudo docker tag rhel7/rhel localhost:5000/myrhel7
    $ sudo docker push localhost:5000/myrhel7

The rest of this section describes how to inspect, copy and get layers from the RHEL 7 image.

Note

The skopeo tool by default requires a TLS connection. It fails when trying to use an unencrypted connection. To override the default and use an http registry, prepend http: to the <registry>/<image> string.

1.5.1. Inspecting container images with skopeo

When you inspect a container image from a registry, you need to identify the container format (such as docker), the location of the registry (such as docker.io or localhost:5000), and the repository/image (such as rhel7/rhel).

The following example inspects the mariadb container image from the Docker Registry:

$ sudo skopeo inspect docker://docker.io/library/mariadb
{
    "Name": "docker.io/library/mariadb",
    "Tag": "latest",
    "Digest": "sha256:d3f56b143b62690b400ef42e876e628eb5e488d2d0d2a35d6438a4aa841d89c4",
    "RepoTags": [
        "10.0.15",
        "10.0.16",
        "10.0.17",
        "10.0.19",
...
    "Created": "2016-06-10T01:53:48.812217692Z",
    "DockerVersion": "1.10.3",
    "Labels": {},
    "Architecture": "amd64",
    "Os": "linux",
    "Layers": [
...

Assuming you pushed a container image tagged localhost:5000/myrhel7 to a docker registry running on your local system, the following command inspects that image:

$ sudo skopeo inspect docker://localhost:5000/myrhel7
{
    "Name": "localhost:5000/myrhel7",
    "Tag": "latest",
    "Digest": "sha256:4e09c308a9ddf56c0ff6e321d135136eb04152456f73786a16166ce7cba7c904",
    "RepoTags": [
        "latest"
    ],
    "Created": "2016-06-16T17:27:13Z",
    "DockerVersion": "1.7.0",
    "Labels": {
        "Architecture": "x86_64",
        "Authoritative_Registry": "registry.access.redhat.com",
        "BZComponent": "rhel-server-docker",
        "Build_Host": "rcm-img01.build.eng.bos.redhat.com",
        "Name": "rhel7/rhel",
        "Release": "75",
        "Vendor": "Red Hat, Inc.",
        "Version": "7.2"
    },
    "Architecture": "amd64",
    "Os": "linux",
    "Layers": [
        "sha256:16dc1f96e3a1bb628be2e00518fec2bb97bd5933859de592a00e2eb7774b6ecf"
    ]
}

1.5.2. Copying container images with skopeo

This command copies the myrhel7 container image from a local registry into a directory on the local system:

# skopeo copy docker://localhost:5000/myrhel7 dir:/root/test/
INFO[0000] Downloading myrhel7/blobs/sha256:16dc1f96e3a1bb628be2e00518fec2bb97bd5933859de592a00e2eb7774b6ecf
# ls /root/test
16dc1f96e3a1bb628be2e00518fec2bb97bd5933859de592a00e2eb7774b6ecf.tar manifest.json

The result of the skopeo copy command is a tarball (16d*.tar) and a manifest.json file representing the image begin copied to the directory you identified. If there were multiple layers, there would be multiple tarballs. The skopeo copy command can also copy images to another registry. If you need to provide a signature to write to the destination registry, you can do that by adding a --sign-by= option to the command line, followed by the required key-id.

1.5.3. Getting image layers with skopeo

The skopeo layers command is similar to skopeo copy, with the difference being that the copy option can copy an image to another registry or to a local directory, while the layers option just drops the layers (tarballs and manifest.jason file) in the current directory. For example

# skopeo layers docker://localhost:5000/myrhel7
INFO[0000] Downloading myrhel7/blobs/sha256:16dc1f96e3a1bb628be2e00518fec2bb97bd5933859de592a00e2eb7774b6ecf
# find .
./layers-myrhel7-latest-698503105
./layers-myrhel7-latest-698503105/manifest.json
./layers-myrhel7-latest-698503105/16dc1f96e3a1bb628be2e00518fec2bb97bd5933859de592a00e2eb7774b6ecf.tar

As you can see from this example, a new directory is created (layers-myrhel7-latest-698503105) and, in this case, a single layer tarball and a manifest.json file are copied to that directory.

1.6. Building container images with Buildah

The buildah command lets you create container images from a working container, a Dockerfile, or from scratch. The resulting images are OCI compliant, so they will work on any runtimes that meet the OCI Runtime Specification (such as Docker and CRI-O).

This section describes how to use the buildah command to create and otherwise work with containers and container images.

1.6.1. Understanding Buildah

Using Buildah is different from building images with the docker command in the following ways:

  • No Daemon!: Buildah bypasses the Docker daemon! So no container runtime (Docker, CRI-O, or other) is needed to use Buildah.
  • Base image or scratch: Lets you not only build an image based on another container, but also lets you start with an empty image (scratch).
  • Build tools external: Doesn’t include build tools within the image itself. As a result, Buildah:

    • Reduces the size of images you build
    • Makes the image more secure by not having the software used to build the container (like gcc, make, and dnf) within the resulting image.
    • Creates images that require fewer resources to transport the images (because they are smaller).

Buildah is able to operate without Docker or other container runtimes by storing data separately and by including features that let you not only build images, but run those images as containers as well. By default, Buildah stores images in an area identified as containers-storage (/var/lib/containers). When you go to commit a container to an image, you can export that container as a local Docker image by indicating docker-daemon (stored in /var/lib/docker).

Note

The containers-storage location that the buildah command uses by default is the same place that the CRI-O container runtime uses for storing local copies of images. So images pulled from a registry by either CRI-O or Buildah, or committed by the buildah command, should be visible to both.

There are more than a dozen options to use with the buildah command. Some of the main activities you can do with the buildah command include:

  • Build a container from a Dockerfile: Use a Dockerfile to build a new container image (buildah bud).
  • Build a container from another image or scratch: Build a new container, starting with an existing base image (buildah from <imagename>) or from scratch (buildah from scratch)
  • Inspecting a container or image: View metadata associated with the container or image (buildah inspect)
  • Mount a container: Mount a container’s root filesystem to add or change content (buildah mount).
  • Create a new container layer: Use the updated contents of a container’s root filesystem as a filesystem layer to commit content to a new image (buildah commit).
  • Unmount a container: Unmount a mounted container (buildah umount).
  • Delete a container or an image: Remove a container (buildah rm) or a container image (buildah rmi).

The buildah package is technology preview for Red Hat Enterprise Linux version 7.4.4. For more details on Buildah, see the GitHub Buildah page. The GitHub Buildah site includes man pages and software that might be more recent than is available with the RHEL version. Here are some other articles on Buildah that might interest you:

1.6.2. Installing Buildah

The buildah package is available from the Red Hat Enterprise Linux Server Extras repository. From a RHEL Server system with a valid subscription, install the buildah package as follows:

# subscription-manager repos --enable=rhel-7-server-rpms
# subscription-manager repos --enable=rhel-7-server-extras-rpms
# yum -y install buildah

With the buildah package installed, you can refer to the man pages included with the buildah package for details on how to use it. To see the available man pages and other documentation, type:

# rpm -qd buildah

The following sections describe how to use buildah to get containers, build a container from a Dockerfile, build one from scratch, and manage containers in various ways.

1.6.3. Getting Images with buildah

To get a container image to use with buildah, use the buildah from command. Here’s how to get a RHEL 7 image from the Red Hat Registry as a working container to use with the buildah command:

# buildah from docker://registry.access.redhat.com/rhel7/rhel-minimal
Getting image source signatures
Copying blob…
Writing manifest to image destination
Storing signatures
rhel-minimal-working-container
# buildah images
IMAGE ID     IMAGE NAME  CREATED AT             SIZE
1456eedf8101 registry.access.redhat.com/rhel7/rhel-atomic:latest
                         Oct 12, 2017 15:15     74.77 MB
# buildah containers
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME  CONTAINER NAME
dc8f21Ag4a47     *     1456eedf8101 registry.access.redhat.com/rhel7/rhel-atomic:latest
               rhel-atomic-working-container
1456eedf8101 registry.access.redhat.com/rhel7/rhel-minimal:latest
                         Oct 12, 2017 15:15     74.77 MB

# buildah containers
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME  CONTAINER NAME
dc8f21Ag4a47     *     1456eedf8101 registry.access.redhat.com/rhel7/rhel-minimal:latest
               rhel-minimal-working-container

Notice that the result of the buildah from command is an image (registry.access.redhat.com/rhel7/rhel-minimal:latest) and a working container that is ready to run from that image (rhel-minimal-working-container). Here’s an example of how to execute a command from that container:

# buildah run rhel-minimal-working-container cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.4 (Maipo)

The image and container are now ready for use with Buildah.

1.6.4. Building an Image from a Dockerfile with Buildah

With the buildah command, you can create a new image from a Dockerfile. The following steps show how to build an image that includes a simple script that is executed when the image is run.

This simple example starts with two files in the current directory: Dockerfile (which holds the instructions for building the container image) and myecho (a script that echoes a few words to the screen):

# ls
Dockerfile  myecho
# cat Dockerfile
FROM registry.access.redhat.com/rhel7/rhel-minimal
ADD myecho /usr/local/bin
ENTRYPOINT "/usr/local/bin/myecho"
# cat myecho
echo "This container works!"
# chmod 755 myecho

With the Dockerfile in the current directory, build the new container as follows:

# buildah bud -t myecho .
STEP 1: FROM registry.access.redhat.com/rhel7/rhel-minimal
STEP 2: ADD myecho /usr/local/bin
STEP 3: ENTRYPOINT "/usr/local/bin/myecho"
STEP 4: COMMIT containers-storage:[devicemapper@/var/lib/containers/storage+/var/run/containers/storage]docker.io/library/myecho:latest

The buildah bud command creates a new image named myecho, but doesn’t create a working container, as demonstrated when you run buildah containers below:

# buildah images
IMAGE ID     IMAGE NAME          CREATED AT          SIZE
1456eedf8101 registry.access.redhat.com/rhel7/rhel-minimal:latest
                                 Oct 12, 2017 15:15  74.77 MB
ab230ac5aba3  docker.io/library/myecho:latest
                                 Oct 12, 2017 15:15  2.854 KB
# buildah containers

Next, you can make the image into a container and run it, to make sure it is working.

1.6.5. Running a Container with Buildah

To check that the image you built previously works, you need to create a working container from the image, then use buildah run to run the working container.

# buildah from myecho
myecho-working-container
# buildah containers
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME  CONTAINER NAME
dc8f21af4a47     *     1456eedf8101  registry.access.redhat.com/rhel7/rhel-minimal:latest
    rhel-minimal-working-container
6d1ffccb557d     *     ab230ac5aba3 docker.io/library/myecho:latest
    myecho-working-container
# buildah run myecho-working-container
This container works!

The steps just shown used the image (myecho) to create a container (myecho-working-container). After that, buildah containers showed the container exists and buildah run ran the container, producing the output: This container works!

1.6.6. Inspecting a Container with buildah

With buildah inspect, you can show information about a container or image. For example, to inspect the myecho image you created earlier, type:

# buildah inspect myecho | less
{
    "type": "buildah 0.0.1",
    "image": "docker.io/library/myecho:latest",
    "image-id": "e2b190ac8a37737ec03cfa4c9bfd989845b9bec3aa81ff48d8350d7418d748f6",
    "config": "eyJjcmVh...
    "ociv1": {
        "created": "2017-10-12T15:15:00.207103Z",
        "author": "Red Hat, Inc.",
        "architecture": "amd64",
        "os": "linux",
        "config": {
            "Entrypoint": [
                "/bin/sh",
                "-c",
                "\"/usr/local/bin/myecho\""
            ],
            "WorkingDir": "/",
            "Labels": {
                "architecture": "x86_64",
                "authoritative-source-url": "registry.access.redhat.com",

To inspect a container from that same image, type the following:

# buildah inspect myecho-working-container | less
{
    "type": "buildah 0.0.1",
    "image": "docker.io/library/myecho:latest",
    "image-id": "e2b190ac8a37737ec03cfa4c9bfd989845b9bec3aa81ff48d8350d7418d748f6",
    "config": "eyJjcmV...
    "container-name": "myecho-working-container",
    "container-id": "70f22e886310bba26bb57ca7afa39fd19af2791c4c66067cb6206b7c3ebdcd20",
    "process-label": "system_u:system_r:svirt_lxc_net_t:s0:c225,c716",
    "mount-label": "system_u:object_r:svirt_sandbox_file_t:s0:c225,c716",
    "ociv1": {
        "created": "2017-10-12T15:15:00.207103Z",
        "author": "Red Hat, Inc.",
        "architecture": "amd64",

Note that the container output has added information, such as the container name, container id, process label, and mount label to what was in the image.

1.6.7. Modifying a Container to Create a new Image with Buildah

There are several ways you can modify an existing container with the buildah command and commit those changes to a new container image:

  • Mount a container and copy files to it
  • Use buildah copy and buildah config to modify a container

Once you have modified the container, use buildah commit to commit the changes to a new image.

1.6.7.1. Using buildah mount to Modify a Container

After getting an image with buildah from, you can use that image as the basis for a new image. The following text shows how to create a new image by mounting a working container, adding files to that container, then committing the changes to a new image.

Type the following to view the working container you used earlier:

# buildah containers
CONTAINER ID BUILDER IMAGE ID     IMAGE NAME  CONTAINER NAME

dc8f21af4a47   *     1456eedf8101 registry.access.redhat.com/rhel7/rhel-minimal:latest
               rhel-minimal-working-container
6d1ffccb557d   *     ab230ac5aba3 docker.io/library/myecho:latest
               myecho-working-container

Mount the container image and set the mount point to a variable ($mymount) to make it easier to deal with:

# mymount=$(buildah mount myecho-working-container)
# echo $mymount
/var/lib/containers/storage/devicemapper/mnt/176c273fe28c23e5319805a2c48559305a57a706cc7ae7bec7da4cd79edd3c02/rootfs

Add content to the script created earlier in the mounted container:

# echo 'echo "We even modified it."' >> $mymount/usr/local/bin/myecho

To commit the content you added to create a new image (named myecho), type the following:

# buildah commit myecho-working-container containers-storage:myecho2

To check that the new image includes your changes, create a working container and run it:

# buildah images
IMAGE ID     IMAGE NAME     CREATED AT          SIZE
a7e06d3cd0e2 docker.io/library/myecho2:latest
                            Oct 12, 2017 15:15  3.144 KB
# buildah from docker.io/library/myecho2:latest
myecho2-working-container
# buildah run myecho2-working-container
This container works!
We even modified it.

You can see that the new echo command added to the script displays the additional text.

When you are done, you can unmount the container:

# buildah umount myecho-working-container

1.6.7.2. Using buildah copy and buildah config to Modify a Container

With buildah copy, you can copy files to a container without mounting it first. Here’s an example, using the myecho-working-container created (and unmounted) in the previous section, to copy a new script to the container and change the container’s configuration to run that script by default.

Create a script called newecho and make it executable:

# cat newecho
echo "I changed this container"
# chmod 755 newecho

Create a new working container:

# buildah from myecho:latest
myecho-working-container-2

Copy newecho to /usr/local/bin inside the container:

# buildah copy myecho-working-container-2 newecho /usr/local/bin

Change the configuration to use the newecho script as the new entrypoint:

# buildah config myecho-working-container-2 --entrypoint "/bin/sh -c /usr/local/bin/newecho"

Run the new container, which should result in the newecho command being executed:

# buildah run myecho-working-container-2
I changed this container

If the container behaved as you expected it would, you could then commit it to a new image (mynewecho):

# buildah commit myecho-working-container-2 containers-storage:mynewecho

1.6.8. Creating images from scratch with Buildah

Instead of starting with a base image, you can create a new container that holds no content and only a small amount of container metadata. This is referred to as a scratch container. Here are a few issues to consider when choosing to create an image starting from a scratch container with the buildah command:

  • With a scratch container, you can simply copy executables that have no dependencies to the scratch image and make a few configuration settings to get a minimal container to work.
  • To use tools like yum or rpm packages to populate the scratch container, you need to at least initialize an RPM database in the container and add a release package. The example below shows how to do that.
  • If you end up adding a lot of RPM packages, consider using the rhel or rhel-minimal base images instead of a scratch image. Those base images have had documentation, language packs, and other components trimmed out, which can ultimately result in your image being smaller.

This example adds a Web service (httpd) to a container and configures it to run. In the example, instead of committing the image to Buildah (containers-storage which stores locally in /var/lib/containers), we illustrate how to commit the image so it can be managed by the local Docker service (docker-daemon which stores locally in /var/lib/docker). You could just have easily committed it to Buildah, which would let you then push it to a Docker service (docker), a local OSTree repository (ostree), or other OCI-compliant storage (oci). (Type man buildah push for details.)

To begin, create a scratch container:

# buildah from scratch
working-container

This creates just an empty container (no image) that you can mount as follows:

# scratchmnt=$(buildah mount working-container)
# echo $scratchmnt
/var/lib/containers/storage/devicemapper/mnt/cc92011e9a2b077d03a97c0809f1f3e7fef0f29bdc6ab5e86b85430ec77b2bf6/rootfs

Initialize an RPM database within the scratch image and add the redhat-release package (which includes other files needed for RPMs to work):

# rpm --root $scratchmnt --initdb
# yum install yum-utils       (if not already installed)
# yumdownloader --destdir=/tmp redhat-release-server
# rpm --root $scratchmnt -ihv /tmp/redhat-release-server*.rpm

Install the httpd service to the scratch directory:

# yum install -y --installroot=$scratchmnt httpd

Add some text to an index.html file in the container, so you will be able to test it later:

# echo "Your httpd container from scratch worked." > $scratchmnt/var/www/html/index.html

Instead of running httpd as an init service, set a few buildah config options to run the httpd daemon directly from the container:

# buildah config --cmd "/usr/sbin/httpd -DFOREGROUND" working-container
# buildah config --port 80/tcp working-container
# buildah commit working-container docker-daemon:myhttpd:latest

By default, the buildah commit command adds the docker.io repository name to the image name and copies the image to the storage area for your local Docker service (/var/lib/docker). For now, you can use the Image ID to run the new image as a container with the docker command:

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker.io/myhttpd   latest              47c0795d7b0e        9 minutes ago       665.6 MB
# docker run -p 8080:80 -d --name httpd-server 47c0795d7b0e
# curl localhost:8080
Your httpd container from scratch worked.

1.6.9. Removing Images or Containers with Buildah

When you are done with particular containers or images, you can remove them with buildah rm or buildah rmi, respectively. Here are some examples.

To remove the container created in the previous section, you could type the following to see the mounted container, unmount it and remove it:

# buildah containers
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME                       CONTAINER NAME
05387e29ab93     *     c37e14066ac7 docker.io/library/myecho:latest  myecho-working-container
# buildah mount
05387e29ab93 /var/lib/containers/storage/devicemapper/mnt/9274181773a.../rootfs
# buildah umount 05387e29ab93
# buildah rm 05387e29ab93
05387e29ab93151cf52e9c85c573f3e8ab64af1592b1ff9315db8a10a77d7c22

To remove the image you created previously, you could type the following:

# buildah rmi docker.io/library/myecho:latest
untagged: docker.io/library/myecho:latest
ab230ac5aba3b5a0a7c3d2c5e0793280c1a1b4d2457a75a01b70a4b7a9ed415a

1.6.10. Using container registries with Buildah

With Buildah, you can push and pull container images between your local system and public or private container registries. The following examples show how to:

  • Push containers to and pull them from a private registry with buildah.
  • Push and pull container between your local system and the Docker Registry.
  • Use credentials to associated you containers with a registry account when you push them.

Use the skopeo command, in tandem with the buildah command, to query registries for information about container images.

1.6.10.1. Pushing containers to a private registry

Pushing containers to a private container registry with the buildah command works much the same as pushing containers with the docker command. You need to:

  • Set up a private registry (OpenShift provides a container registry or you can set up a simple registry with the docker-distribution package, as shown below).
  • Create or acquire the container image you want to push.
  • Use buildah push to push the image to the registry.

To install a registry on your local system, start it up, and enable it to start on boot, type:

# yum install -y docker-distribution
# systemctl start docker-distribution
# systemctl enable docker-distribution

By default, the docker-distribution service listens on TCP port 5000 on your localhost.

To push an image from your local Buildah container storage, check the image name, then push it it using the buildah push command. Remember to identify both the local image name and a new name that includes the location (localhost:5000, in this case):

# buildah images
IMAGE ID     IMAGE NAME                       CREATED AT          SIZE
cb702d492ee9 docker.io/library/myecho2:latest Nov 21, 2017 16:50     3.143 KB
# buildah push --tls-verify=false myecho2:latest localhost:5000/myecho2:latest
Getting image source signatures
Copying blob sha256:e4efd0...
...
Writing manifest to image destination
Storing signatures

Use the curl command to list the images in the registry and skopeo to inspect metadata about the image:

# curl http://localhost:5000/v2/_catalog
{"repositories":["myatomic","myecho2"]}
# curl http://localhost:5000/v2/myecho2/tags/list
{"name":"myecho2","tags":["latest"]}
# skopeo inspect --tls-verify=false docker://localhost:5000/myecho2:latest | less
{
    "Name": "localhost:5000/myecho2",
    "Digest": "sha256:8999ff6050...",
    "RepoTags": [
        "latest"
    ],
    "Created": "2017-11-21T16:50:25.830343Z",
    "DockerVersion": "",
    "Labels": {
        "architecture": "x86_64",
        "authoritative-source-url": "registry.access.redhat.com",

At this point, any tool that can pull container images from a container registry can get a copy of your pushed image. For example, you could start the docker daemon and try to pull the image so it can be used by the docker command as follows:

# systemctl start docker
# docker pull localhost:5000/myecho2
# docker run localhost:5000/myecho2
This container works!

1.6.10.2. Pushing containers to the Docker Hub

You can use your Docker Hub credentials to push and pull images from the Docker Hub with the buildah command. For this example, replace the username and password (testaccountXX:My00P@sswd) with your own Docker Hub credentials:

# buildah push --creds testaccountXX:My00P@sswd \
     docker.io/library/myecho2:latest docker://testaccountXX/myecho2:latest

As with the private registry, you can then get and run the container from the Docker Hub with either the buildah or docker command:

# docker run docker.io/textaccountXX/myecho2:latest
This container works!
# buildah from docker.io/textaccountXX/myecho2:latest
myecho2-working-container-2
# buildah run myecho2-working-container-2
This container works!