Containerizing a legacy Perl application like Bugzilla comes with the challenge of managing CPAN modules and other dependencies that the app needs. In 2025, Perl is less common in new apps, but the modules Bugzilla requires may not all be packaged for modern OSs by default. Here are best practices to handle this complex dependency stack:

  • Use a Stable Base Image and Package Manager: Start with a known-good base for your Bugzilla image. Many choose a Perl-specific base image (for example, the official perl image on Docker Hub, which is built on Debian or Alpine and includes a specific Perl version). This ensures you have a recent Perl (Bugzilla 5.2 supports Perl 5.24+ typically) and common tools. Using a base like Debian 11 or Ubuntu 20.04 is reasonable, as they still provide packages for many Perl modules. Leverage the OS package manager to install any CPAN modules that are available as packages (these are usually prefixed with lib...-perl). For instance, Bugzilla needs DBI and DBD::mysql – these can often be installed via apt-get install libdbi-perl libdbd-mysql-perl (providing compiled versions). Similarly, modules like Date::Format, Template-Toolkit, etc., might have Debian packages. Installing those via apt can save you from compiling them and ensures binary compatibility. However, not everything Bugzilla needs will be packaged (or the version might be old), so you’ll still use CPAN for the remainder.

  • Automate CPAN Module Installation: Non-packaged modules should be installed in the Dockerfile build. A common approach is using cpanminus (cpanm) along with a cpanfile listing required modules (Bugzilla’s source might include a requirements.txt or documentation of needed modules). You can either manually list out the modules to install or run Bugzilla’s own dependency check script (checksetup.pl) in a mode that installs modules. Bugzilla can be configured to auto-install missing modules via CPAN if you permit it, but in a Docker build you want non-interactive, deterministic installation. Using cpanm is ideal: it can install from CPAN without prompts and can fail the build if something doesn’t install.

  • Cache and Layer Dependencies: Building a Perl image from scratch can be time-consuming, especially if it compiles XS modules (C-based components). To optimize, take advantage of Docker layer caching. For example, you might structure the Dockerfile in two stages or layers: (1) a “CPAN build” stage that installs all needed Perl modules, and (2) the final stage that copies the installed libraries into a runtime image (if you want to keep the runtime image lean without build tools). Another simpler method is to install everything in one layer but ensure that the layer is only invalidated when dependencies change. You could copy in a file like cpanfile and then run cpanm --installdeps . (if Bugzilla were set up as a CPAN distribution) or explicitly cpanm each module. The key is to do this before copying your application code, so that changes to the Bugzilla code don’t bust the cache of the dependencies. This way, rebuilding the image won’t re-download/recompile CPAN modules every time.

  • Consider a “fat” Base Image: Since you have multiple Bugzilla instances to deploy, it might make sense to build a common base image that has all of Bugzilla’s Perl module dependencies pre-installed. For example, you create bugzilla-base:5.2 image which contains Perl, Apache, and all required CPAN modules, but not the specific site configuration or data. Then for each Bugzilla instance, you have a small Dockerfile FROM bugzilla-base:5.2 that just adds the Bugzilla configuration (maybe the localconfig file or any branding customizations) and points to the existing modules. This way, the heavy lift of installing modules (which could be dozens of CPAN distributions) is done once. All Bugzilla containers can share that base, saving disk space and build time. It also means if you update a module or Bugzilla version, you do it in one place. The community has embraced similar ideas – for instance, one community image for Bugzilla “provides a Bugzilla stable version with CPAN modules” baked in so that running the container is plug-and-play. You might even use such images as reference or starting points (e.g. the nasqueron/bugzilla image on Docker Hub includes most dependencies​).

  • Vendoring Dependencies (Carton): For truly repeatable builds, you can use Carton, a Perl dependency management tool, to lock versions of CPAN modules. Mozilla’s Bugzilla team used Carton to vendor all dependencies for bugzilla.mozilla.org – essentially, they checked in a cpanfile.snapshot and all the module tarballs. This approach can be excellent for an air-gapped environment because you can bundle all necessary CPAN packages offline. Consider doing a one-time run of Carton to fetch all Bugzilla deps (on a machine with internet), then copy the local/lib or vendor directory into your Docker build context. Then your Dockerfile can simply add that directory and your Perl @INC can include it, meaning no internet access is needed to install modules. This makes your build self-contained. The FOSDEM 2018 talk “How Carton, Docker, and CircleCI Saved my Sanity” describes how vendoring Perl modules improved reliability for deploying a legacy app– a relevant insight for 2025 where some CPAN mirrors or modules might disappear or break unexpectedly.

  • Slimming the Image: After you’ve installed all modules and the application, consider removing unnecessary build tools. For example, you’ll likely need make, compilers, libraries (like openssl dev, mysql client dev) to build certain CPAN modules. 

    Install them in an earlier step and uninstall them at the end of the Dockerfile (or use multi-stage builds). This reduces the image size and attack surface. Also remove any CPAN build caches that aren’t needed at runtime. The final image should contain Perl, the modules, Apache/httpd (if using it), and Bugzilla’s code – but not the entire CPAN build environment.

  • Configuration in the Image: For Bugzilla, you’ll run an Apache (httpd) with mod_cgi or mod_perl, or possibly use an alternative like Plack/PSGI. The simplest route is to use Apache with mod_cgi (Bugzilla default) or mod_perl2 for performance. In 2025, mod_perl is still available but can be memory-heavy. If memory is constrained, running under plain CGI or FastCGI might be preferable to keep Apache workers lighter (each mod_perl worker loads the entire app in memory). This is a tuning consideration: mod_perl will speed up Bugzilla responses significantly by preloading Perl modules, but uses more RAM per process. Since you might not have a lot of traffic (assuming these Bugzillas are internal tools), you could stick to CGI which forks a Perl interpreter for each request (slower, but lower constant memory). It’s a trade-off: if response times are acceptable, CGI is simpler. If you need more speed and have memory headroom, enable mod_perl2 (and ensure to include the mod_perl2 package and Bugzilla’s mod_perl config adjustments). Either way, ensure the Apache config in the container is tuned (limit MaxClients if memory is low, etc.)

  • Testing the Build: Because of the CPAN complexity, do thorough testing of the container image. Run Bugzilla’s checksetup.pl inside the container to verify all required modules are seen as installed (it will list any missing ones). Also test functions like sending email (Bugzilla uses sendmail or SMTP – you might use a lightweight send-only SMTP client in the container, like msmtp, configured to relay to your SMTP server). If something doesn’t work in the container as it did on a VM, it’s likely a missing dependency or a permission issue.

Handling a legacy tech stack in a modern container requires embracing the mantra: “build once, run anywhere.” By pre-building all Perl modules into the container image, you ensure that the runtime environment is consistent and doesn’t need internet or manual intervention. This approach has been proven in real-world migrations – teams have found that investing time in setting up the Dockerfile with all dependencies pays off with deployments that “just work” in production, even in isolated networks. It can be frustrating to wrestle with CPAN, but with the right strategy (using system packages, cpanminus, and caching layers), you can tame the Perl dependency monster.


Video available:

https://archive.fosdem.org/2018/schedule/event/perl_docker_mozilla/
 

Â