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_hostname
      parameter 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_tar compresses the debian TAR archives with Gzip before
      storing. You're trading build speeds in for less disk space.

      os_version controls which Debian Packages will be used to find the
      packages on the mirror.

      When cache_folder is 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_cache will look for a Packages file defined by
      packages_file option. 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.tar file 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.tar file 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_name is the name of the Debian package,
      $files_to_extract is 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_disk inside the container at location
      $location_in_ctr with the specified $mode, $user and $group.

    add_file_from_string($data, $location_in_ctr, $mode, $user, $group)

      Adds the data in the scalar $data to the container at location
      $location_in_ctr with the specified $mode, $user and $group.

    copy($local_dirpath, $location_in_ctr, $mode, $user, $group)

      Recursively copy the $local_dirpath directory into a layer of the
      container. The resulting path inside the container is defined by
      $location_in_ctr. $mode controls the directory permission of
      $location_in_ctr only. Inner directories will have the permissions as
      on the local filesystem. All directories and files will be changed to
      be owned by $user and $group.

      If $location_in_ctr has a slash at the end, the last directory of
      $local_dirpath will become a subdirectory of the path
      $location_in_ctr. Otherwise, the last directory of $local_dirpath
      will be renamed to the last directory of $location_in_ctr.

      For example copy('lib/', '/app/') will create /app/lib/ but
      copy('lib/', '/app') will put all put the files and directories
      directly inside /app, there will be no lib directory.

    create_directory($path, $mode, $uid, $gid)

      Create an empty directory at $path inside the container with the
      specified $mode, $user and $group.

    add_user($name, $uid, $main_gid, $shell, $homedir)

      Add a user to the container. This puts the user inside the
      /etc/passwd file.

    add_group($name, $gid)

      Add a group to the container. This puts the group inside the
      /etc/group file.

    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::Layer objects 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 <https://github.com/GoogleContainerTools/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).

