Using distrobuilder to create container images for LXC and LXD

With LXC and LXD you can run system containers, which are containers that behave like a full operating system (like a Virtual Machine does). There are already official container images for most Linux distributions. When you run lxc launch ubuntu:18.04 mycontainer, you are using the ubuntu: repository of container images to launch a container with Ubuntu 18.04.

In this post, we are going to see

  1. an introduction to the tool distrobuilderthat creates container images
  2. how to recreate a container image
  3. how to customize a container image

Introduction to distrobuilder

The following are the command line options of distrobuilder. You can use distrobuilder to create container images for both LXC and LXD.

$ distrobuilder
System container image builder for LXC and LXD

  distrobuilder [command]

Available Commands:
  build-dir   Build plain rootfs
  build-lxc   Build LXC image from scratch
  build-lxd   Build LXD image from scratch
  help        Help about any command
  pack-lxc    Create LXC image from existing rootfs
  pack-lxd    Create LXD image from existing rootfs

      --cache-dir   Cache directory
      --cleanup     Clean up cache directory (default true)
  -h, --help        help for distrobuilder
  -o, --options     Override options (list of key=value)

Use "distrobuilder [command] --help" for more information about a command.

The build-dir command builds the root filesystem (rootfs) of the distribution and stops there. This option makes sense if we plan to make some custom manual changes to the rootfs. We would then need to use either pack-lxc or pack-lxd to package up the rootfs into a container image.

The build-lxc and build-lxd commands create container images for either LXC or LXD, both from scratch. They both require a YAML configuration file, and that’s what is only needed from them to produce  a container image.


Currently, there are no binary packages of distrobuilder. Therefore, you will need to compile it from source. To do so, first install the Go programming language, and some other dependencies. Here are the commands to do this.

sudo apt update
sudo apt install -y golang-go debootstrap rsync gpg squashfs-tools

Second, download the source code of the distrobuilder repository (this repository). The source will be placed in $HOME/go/src/ is the command to do this.

go get -d -v

Third, enter the directory with the source code of distrobuilder and run make to compile the source code. This will generate the executable program distrobuilder, and it will be located at $HOME/go/bin/distrobuilder. Here are the commands to do this.

cd $HOME/go/src/

Creating a container image

To create a container image, first create a directory where you will be placing the container images, and enter that directory.

mkdir -p $HOME/ContainerImages/ubuntu/
cd $HOME/ContainerImages/ubuntu/

Then, copy one of the example yaml configuration files for container images into this directory. In this example, we are creating an Ubuntu container image.

cp $HOME/go/src/ ubuntu.yaml

Finally, run distrobuilder to create the container image. We are using the build-lxd option to create a container image for LXD. We need sudo because the process of preparing the rootfs requires to set the ownership and permissions of files to IDs that a non-root account cannot perform. Also note the way we invoke distrobuilder (as $HOME/go/bin/distrobuilder). It has to be an absolute path because under sudo the $PATH is different from our current non-root user account.

sudo $HOME/go/bin/distrobuilder build-lxd ubuntu.yaml

It takes about five minutes to build the Ubuntu container image. Be patient.

If the command is successful, you will get an output similar to the following. The lxd.tar.xz file is the description of the container image. The rootfs.squasfs file is the root filesystem (rootfs) of the container image. The set of these two files is the container image.

multipass@dazzling-termite:~/ContainerImages/ubuntu$ ls -l
total 121032
-rw-r--r-- 1 root      root            560 Oct  3 13:28 lxd.tar.xz
-rw-r--r-- 1 root      root      123928576 Oct  3 13:28 rootfs.squashfs
-rw-rw-r-- 1 multipass multipass      3317 Oct  3 13:19 ubuntu.yaml

Adding the container image to LXD

To add the container image to a LXD installation, use the lxc image import command as follows.

multipass@dazzling-termite:~/ContainerImages/ubuntu$ lxc image import lxd.tar.xz rootfs.squashfs --alias mycontainerimage
Image imported with fingerprint: ae81c04327b5b115383a4f90b969c97f5ef417e02d4210d40cbb17a038729a27

Let’s see the container image in LXD. The ubuntu.yaml had a setting to create an Ubuntu 17.10 (artful) image. The size is 118MB.

$ lxc image list mycontainerimage
|      ALIAS       | FINGERPRINT  | PUBLIC |  DESCRIPTION  |  ARCH  |   SIZE   |         UPLOAD DATE          |
| mycontainerimage | ae81c04327b5 | no     | Ubuntu artful | x86_64 | 118.19MB | Oct 3, 2018 at 12:09pm (UTC) |

Launching a container from the container image

To launch a container from the freshly created container image, use lxc launch as follows. Note that you do not specify a repository of container images (like ubuntu: or images:) because the image is located locally.

$ lxc launch mycontainerimage c1
Creating c1
Starting c1

How to customize a container image

The ubuntu.yaml configuration file contains all the details that are required to create an Ubuntu container image. We can edit the file and make changes to the generated container image.

Changing the distribution release

The file that is currently included in the distrobuilder repository has the following section:

distribution: ubuntu
release: artful
description: Ubuntu {{ image.release }}
architecture: amd64

We can change to either bionic (for Ubuntu 18.04) or cosmic (for Ubuntu 18.10), save and finally build again the container image.


Error “gpg: no valid OpenPGP data found”

$ sudo $HOME/go/bin/distrobuilder build-lxd ubuntu.yaml
Error: Error while downloading source: Failed to create keyring: gpg: keyring /tmp/distrobuilder.920564219/secring.gpg' created gpg: keyring/tmp/distrobuilder.920564219/pubring.gpg' created
gpg: requesting key C0B21F32 from hkp server
gpgkeys: key 790BC7277767219C42C86F933B4FE6ACC0B21F32 can't be retrieved
gpg: no valid OpenPGP data found.
gpg: Total number processed: 0
gpg: keyserver communications error: keyserver helper general error
gpg: keyserver communications error: unknown pubkey algorithm
gpg: keyserver receive failed: unknown pubkey algorithm

The keyserver is often under load and does not respond. You can edit the YAML configuration file and replace with

Error “gpg: keyserver timed out”

$ sudo $HOME/go/bin/distrobuilder build-lxd ubuntu.yaml
Error: Error while downloading source: Failed to create keyring: gpg: keyring /tmp/distrobuilder.854636592/secring.gpg' created gpg: keyring/tmp/distrobuilder.854636592/pubring.gpg' created
gpg: requesting key C0B21F32 from hkp server
gpg: keyserver timed out
gpg: keyserver receive failed: keyserver error

The keyserver is often under load and does not respond. You can edit the YAML configuration file and replace with

Permanent link to this article:


1 ping

  1. Hi Simos, is there any way to include the installation of LXD when generating an Ubuntu image?
    I believe that adding “snapd” to the apt packages list in the yaml file will work, but will a “snap install lxd” command defined in the “action” section work? I have seen issues when trying to install anything using snap when running from a chroot environment from a debootstrap generated file system.

    • prot on May 2, 2020 at 18:43
    • Reply

    this article is saving my behind now 😉

  1. […] Using distrobuilder to create container images for LXC and LXD […]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: