NAME
Container::Builder - Build Container archives.
SYNOPSIS
# See also the examples/ folder of this module.
use v5.40;
use Container::Builder;
# Please use a Debian mirror close to you
my $builder = Container::Builder->new(debian_pkg_hostname => 'debian.inf.tu-dresden.de');
$builder->create_directory('/', 0755, 0, 0);
$builder->create_directory('bin/', 0755, 0, 0);
$builder->create_directory('tmp/', 01777, 0, 0);
$builder->create_directory('root/', 0700, 0, 0);
$builder->create_directory('home/', 0755, 0, 0);
$builder->create_directory('home/larry/', 0700, 1337, 1337);
$builder->create_directory('etc/', 0755, 0, 0);
$builder->create_directory('app/', 0755, 1337, 1337);
# C dependencies (to run a compiled executable)
$builder->add_deb_package('libc-bin');
$builder->add_deb_package('libc6');
$builder->add_deb_package('gcc-12-base');
$builder->add_deb_package('libgcc-s1');
$builder->add_deb_package('libgomp1');
$builder->add_deb_package('libstdc++6');
# Perl base
$builder->add_deb_package('libcrypt1');
$builder->add_deb_package('perl-base');
$builder->add_group('root', 0);
$builder->add_group('tty', 5);
$builder->add_group('staff', 50);
$builder->add_group('larry', 1337);
$builder->add_group('nobody', 65000);
$builder->add_user('root', 0, 0, '/sbin/nologin', '/root');
$builder->add_user('nobody', 65000, 65000, '/sbin/nologin', '/nohome');
$builder->add_user('larry', 1337, 1337, '/sbin/nologin', '/home/larry');
$builder->runas_user('larry');
$builder->set_env('PATH', '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin');
$builder->set_work_dir('/home/larry/');
$builder->set_entry('perl', 'testproggie.pl');
my $testproggie = <<'PROG';
use v5.36;
say "Hallo vriendjes en vriendinnetjes!";
PROG
$builder->add_file_from_string($testproggie, '/home/larry/testproggie.pl', 0644, 1337, 1337); # our program
$builder->build('01-hello-world.tar');
say "Now run: podman load -i 01-hello-world.tar";
say "Then run: podman run " . substr($builder->get_digest(), 0, 12);
DESCRIPTION
Container::Builder builds a TAR archive that can be imported into Podman or Docker. It's main use is to craft specific, small containers based on Debian package (.deb) files. The type of functions to extend are similar to those that you can find in a Dockerfile.
We use a Build pattern to build the archive. Most functions return quickly, and only the build() function actually creates all the layers of the container and writes the result to disk.
Look into the examples/ folder for some examples to make working Perl (Dancer2) images.
Note: This module is not production-ready! It's still in early stages of development and maturity.
METHODS
- new(debian_pkg_hostname => 'mirror.as35701.net', [compress_deb_tar => 1], [os_version => 'bookworm'], [cache_folder => 'artifacts/'], [enable_packages_cache => 0], [packages_file => 'Packages'])
-
the square brackets signify that the parameter is optional, not an array ref
Create a Container::Builder object. Only the
debian_pkg_hostnameparameter is required so you can pick a Debian mirror close to the geographical region from where the code is running. See https://www.debian.org/mirror/list.compress_deb_tarcompresses the debian TAR archives with Gzip before storing. You're trading build speeds in for less disk space.os_versioncontrols which Debian Packages will be used to find the packages on the mirror.When
cache_folderis defined, the folder will be used to store the downloaded deb packages and it will be used in subsequent runs as a cache so we don't retrieve it from the debian mirror every single time.enable_packages_cachewill look for a Packages file defined bypackages_fileoption. If it doesn't exist, it will be downloaded from the Debian mirror. If it does exist, it will be read from disk instead of getting a fresh copy. - add_deb_package('libperl5.36')
-
Add a Debian package to the container. The
data.tarfile inside the Debian package file (.deb) will be stored as a layer in the resulting container. - add_deb_package_from_file($filepath_deb)
-
Add a Debian package file to the container. The
data.tarfile inside the Debian package file (.deb) will be stored as a layer in the resulting container. - extract_from_deb($package_name, $files_to_extract)
-
Extract certain files from the Debian package before storing as a layer.
$package_nameis the name of the Debian package,$files_to_extractis an array ref containing a list of files to extract. Rudimentary support for globs/wildcards (only useable at the end of the string).This is an experimental method.
- add_file($file_on_disk, $location_in_ctr, $mode, $user, $group)
-
Adds the local file
$file_on_diskinside the container at location$location_in_ctrwith the specified$mode,$userand$group. - add_file_from_string($data, $location_in_ctr, $mode, $user, $group)
-
Adds the data in the scalar
$datato the container at location$location_in_ctrwith the specified$mode,$userand$group. - copy($local_dirpath, $location_in_ctr, $mode, $user, $group)
-
Recursively copy the
$local_dirpathdirectory into a layer of the container. The resulting path inside the container is defined by$location_in_ctr.$modecontrols the directory permission of$location_in_ctronly. Inner directories will have the permissions as on the local filesystem. All directories and files will be changed to be owned by$userand$group.If
$location_in_ctrhas a slash at the end, the last directory of$local_dirpathwill become a subdirectory of the path$location_in_ctr. Otherwise, the last directory of$local_dirpathwill be renamed to the last directory of$location_in_ctr.For example
copy('lib/', '/app/')will create/app/lib/butcopy('lib/', '/app')will put all put the files and directories directly inside/app, there will be nolibdirectory. - create_directory($path, $mode, $uid, $gid)
-
Create an empty directory at
$pathinside the container with the specified$mode,$userand$group. - add_user($name, $uid, $main_gid, $shell, $homedir)
-
Add a user to the container. This puts the user inside the
/etc/passwdfile. - add_group($name, $gid)
-
Add a group to the container. This puts the group inside the
/etc/groupfile. - runas_user($user)
-
Specify the user to run the entrypoint as.
- set_env($key, $value)
-
Add a environment variable to the container definition.
- set_entry(@command_str)
-
Set the default entrypoint of the container.
- set_work_dir($workdirectory)
-
Set the default working directory of the container.
- build()
- build('mycontainer.tar')
-
Build the container and write the result to the filepath specified. If no argument is given, the entire archive is returned as a scalar from the method.
- get_digest()
-
Returns the digest of the embedded config file in the archive. This digest is used by tools such as podman as a unique ID to your container.
- get_layers()
-
Returns a list of
Container::Builder::Layerobjects as currently added to the Builder.Note: During build() extra layers can be added in the front or at the end of this list.
AUTHOR
Adriaan Dens <adri@cpan.org>
COPYRIGHT
Copyright 2026- Adriaan Dens
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
SEE ALSO
Google distroless containers are the main inspiration for creating this module. The idea of creating minimal containers based on Debian packages comes from the Bazel build code in the linked repository that uses these packages to provide a minimal working container. My own examples do the same (and were an initial experiment to see if this approach would actually work).