9 July 2026

Migrating from CentOS 7 to RHEL 9 while keeping Varnish 3: an RPM rebuild born from a real need

Varnish 3 for RHEL 9 was born from an RPM rebuild needed to migrate legacy systems without immediately rewriting complex production VCLs.

In an enterprise Linux migration, the problem is rarely simply upgrading the operating system version. The real problem is everything that's been built on top of that system over the years: legacy packages, no longer available dependencies, historical init scripts, application configurations, implicit assumptions, and components that, despite being dated, continue to play a critical role in production.

This is the case of a customer who needed to migrate 43 systems from CentOS 7 to RHEL 9, while maintaining a very binding technical requirement: to continue using Varnish Cache 3.0.7.

The reason was not an arbitrary preference for legacy software. The infrastructure used numerous Custom VCL 3.x, developed over the years, often thousands of lines long and deeply integrated with the application logic. These VCLs handled caches, bypasses, backends, headers, request normalization, exceptions, special behaviors, and specific business logic.

Rewriting them for Varnish 4, Varnish 6, or later didn't mean changing any guidelines. It meant starting a separate project for analysis, porting, functional testing, load testing, application validation, and a thorough comparison of the old and new behavior.

For this reason we have chosen a pragmatic approach: Migrate your operating system to RHEL 9 without changing Varnish 3 behaviorTo achieve this, however, simply installing the old EL7 RPMs wasn't enough. On EL9, those packages no longer worked properly. There was only one solution: recover the official source code and rebuild the package cleanly for RHEL 9 and derivatives.

The Varnish 3 rebuild GitHub repository for EL9

The result of this work has been published in a dedicated GitHub repository:

Repositories: https://github.com/MarcoMarcoaldi/VARNISH3-EL9

The repository contains the rebuild of the official RPM packages of Varnish Cache 3.0.7 for RHEL 9, AlmaLinux 9 and Rocky Linux 9, based on the original Source RPM varnish-3.0.7-1.el7.centos.src.rpm published by Varnish Software.

This is not a functional fork of Varnish, nor is it a modernized version of the software. It's a targeted rebuild, with minimal patches and changes to the RPM spec, designed for a specific use case: Maintain compatibility with legacy VCL 3.x configurations during a migration to EL9.

This distinction is fundamental. Varnish 3 is software End of life Since 2015, it hasn't received any upstream security fixes and shouldn't be used for new deployments. For new projects, it's OK to use Varnish 6.0 LTS or later. In this case, however, the requirement wasn't to design a new caching architecture, but to allow 43 existing systems to migrate from CentOS 7 without breaking thousands of lines of VCL already in production.

Why Varnish 4 broke compatibility with Varnish 3

The main technical issue is the compatibility breach introduced starting from Varnish 4The VCL language has not remained unchanged: it has been substantially restructured, with incompatible changes from the Varnish 3 syntax and logical model.

Some examples make the scope of the change immediately clear:

Varnish 3 Varnish 4 and later Impact
vcl_fetch vcl_backend_response Change the point in the flow where the response is handled by the backend
req.request req.method Change the variable used to read the HTTP method
error synth Change the logic for generating synthetic responses
Actions return VCL 3 Actions return rearranged Change the behavior of the decision flow

In a simple configuration, these changes can be addressed with relatively little manual porting. In an enterprise configuration, with thousands of VCL lines and years of layered logic, the situation is different.

A production VCL isn't just a configuration file. It's often an integral part of application behavior. It decides when a request should be cached, when it should be passed to the backend, when a cookie should cause a bypass, when a header should be removed, when a URL should be normalized, when a response should have differentiated TTLs, or when an error should be transformed into a synthetic response.

Mistranslating even one of these logics can lead to serious problems: caching of private content, missed bypasses, backend overload, API errors, lost application headers, unexpected changes in TTLs, or hard-to-diagnose differences in client-side behavior.

For this reason, in this project, porting to VCL 4 or later was deliberately excluded from the operating system migration. It would have been technically possible, but it wasn't reasonable within the same project window.

Why old RPM EL7s couldn't be reused on EL9s

The natural first step when maintaining a legacy version is to check whether existing packages can be installed on the new operating system. In our case, the historical package was varnish-3.0.7-1.el7.centos.

On EL8, some dependencies were still available through compatibility packages. On EL9, however, the old binary broke for structural reasons. Runtime dependencies required by the EL7 package were no longer present or could no longer be resolved in the same way.

EL7 Runtime Dependency Situation on EL8 Situation on EL9
libncurses.so.5 / libtinfo.so.5 Available via ncurses-compat-libs Removed
libnsl.so.1 Available via libnsl Removed, available only libnsl.so.2
/sbin/chkconfig e /sbin/service Dependencies still resolvable Unresolvable dependencies due to UsrMove

This ruled out a superficial adaptation. Creating symlinks, forcing installation, or ignoring dependencies wasn't enough. The problem was deeper: the binary had been compiled for a different runtime ecosystem.

The conclusion was clear: needed to recompile Varnish 3.0.7 from source, generating RPMs consistent with RHEL 9, AlmaLinux 9 and Rocky Linux 9.

Retrieving the official Source RPM

The work started from the original Source RPM declared in the metadata of historical packages: varnish-3.0.7-1.el7.centos.src.rpm.

The old historical repository repo.varnish-cache.org it was no longer available, but the Source RPM was still available in the repository varnishcache/varnish30 on packagecloud. Once downloaded and extracted, the package contained the two essential elements needed to proceed:

  • varnish-3.0.7.tar.gz, that is, the upstream source tarball;
  • varnish.spec, that is, the original RPM spec file for EL7.

From there, the adaptation work began. The goal wasn't to alter Varnish's behavior, but to apply the minimum set of changes necessary to obtain a package that would compile and, most importantly, actually run on EL9.

Release 3.0.7-2: First RPM spec adaptation to EL9

The first release of the rebuild, 3.0.7 - 2, involved the initial adaptation of the spec and source to the EL9 build environment.

The first problem was in the source code included in the tarball. The historical version of jemalloc incorporated by Varnish 3 included the header <sys/sysctl.h>, removed in modern versions of glibc. On EL9, which is based on modern glibc, that reference prevented compilation.

A minimal patch was then applied to remove the no longer available include. This isn't a functional change, but a necessary fix to allow the 2015 source code to be compiled in a 2020+ environment.

The spec file then required several adjustments:

  • python it has been replaced with python3 in BuildRequires;
  • groff it has been replaced with groff-base;
  • libedit-devel it was managed via CRB repository;
  • Scriptlet dependencies have been expressed by package name, no longer by path /sbin/*;
  • LTO has been disabled;
  • the test suite was made opt-in via --with check;
  • rpmdevtools rpath check has been disabled, because RPATH to private libraries in /usr/lib64/varnish it is necessary.

An important point concerns scriptlets. Old packages relied on dependency files like /sbin/chkconfig e /sbin/serviceOn EL9, with UsrMove, this model is no longer reliable. The correct solution was to declare dependencies on packages like chkconfig, initscripts e initscripts-service.

SysV scripts have been retained for compatibility with EL7. This has preserved existing operational behavior, including the use of /etc/sysconfig/varnish.

SysV init on EL9: legacy choice, but useful for compatibility

Varnish 3 was born at a time when systemd integration wasn't the dominant model. To maintain maximum compatibility with the previous environment, the SysV scripts were left largely unchanged.

On EL9 the service can still be managed via systemd thanks to systemd-sysv-generator, which dynamically generates systemd units from legacy SysV scripts.

systemctl enable --now varnish
systemctl status varnish

This allows you to maintain familiar service management without immediately rewriting the entire init layer. This solution is consistent with the project's goal: avoiding unnecessary changes while migrating the operating system.

However, there remains a known limitation. This choice is suitable for EL9, but it shouldn't be considered an indefinite future strategy. In future scenarios, especially considering RHEL 10 and beyond, native systemd units will be necessary.

Release 3.0.7-3: SIGSEGV Crash Caused by jemalloc

After the first rebuild, the package was built and installed. The next problem only emerged at runtime: varnishd crashed immediately on startup with a SIGSEGV.

This was one of the most interesting parts of the troubleshooting process, because the build was formally correct. The problem didn't appear during compilation, but only when the daemon was running on EL9.

Debugging with gdb, supported by packages -debuginfo produced during the build, showed the real chain:

varnishd
-> getgrnam("varnish")
-> nss-systemd
-> malloc_usable_size(NULL)
-> jemalloc legacy
-> SIGSEGV

In the startup phase, varnishd solves the group varnish means getgrnam("varnish"). On EL9 comes into play nss-systemd, which can be called malloc_usable_size(NULL). This behavior is legal for glibc.

The problem is that Varnish 3 incorporated a very old version of jemalloc, dating back to 2008, which intercepted process-level allocation functions. Its implementation of malloc_usable_size() did not handle the pointer correctly NULLThe result was an immediate segfault, even before varnishd could print a useful message.

The fix applied in the release 3.0.7 - 3 was compiling Varnish with:

--without-jemalloc

In this way varnishd Uses glibc's modern malloc, bypassing the old embedded jemalloc. This fixed the startup crash on EL9.

Release 3.0.7-4: VCL not compiling on clean EL9 hosts

Once the crash was fixed, a second, even more subtle runtime issue emerged. The service would start correctly on the build machine, but would fail on a minimal, clean EL9 host when compiling the VCL.

The error was as follows:

gcc: fatal error: cannot read spec file '/usr/lib/rpm/redhat/redhat-hardened-cc1'

To understand the problem, we need to remember an architectural detail of Varnish: Varnish compiles VCLs at runtime into C code. This means that gcc It's not just a build dependency, but also a runtime dependency. Each VCL is transformed into C code, compiled, and loaded by the daemon.

During the RPM build phase on EL9, the command cc registered by Varnish incorporated flags derived from the RPM environment, including references to:

-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1

That file is present on the build machine, where it is installed redhat-rpm-configBut it's not normally present on a minimal production server. So the package seemed to work on the build machine, but failed when deployed to a clean system.

The fix applied in the release 3.0.7 - 4 it was forcing a command VCC_CC clean and portable when configuring:

export VCC_CC='exec gcc -std=gnu99 -O2 -g -fpic -shared -Wl,-x -o %o %s'

In this way varnishd It registers a runtime compilation command that is independent of the files present only on the build machine. The VCL can therefore also be compiled on minimal EL9 hosts, as long as it is installed. gcc, required for design.

Installing RPM packages

Once the RPMs are produced or downloaded from the Releases page of the repository, the installation is done via dnf, leaving the package manager to automatically resolve dependencies:

cd /path/to/downloaded/rpms/
dnf install ./varnish*.rpm

Varnish 3 RHEL9

After installation, the service can be enabled and started normally:

systemctl enable --now varnish
curl -I http://localhost:6081/

Varnish 3 Almalinux 9

The configuration remains in the historical paths used on EL7:

/etc/varnish/default.vcl
/etc/sysconfig/varnish

This is a very important practical aspect in a 43-system migration. Maintaining the same paths reduces operational differences, simplifies rollout, and allows for the reuse of existing procedures, templates, and automations.

Build from source on AlmaLinux 9 or Rocky Linux 9

The repository also allows you to rebuild packages locally. On an AlmaLinux 9 or Rocky Linux 9 machine, the flow is the classic RPM flow:

dnf install -y rpm-build rpmdevtools gcc make 
ncurses-devel libxslt groff-base pcre-devel pkgconf-pkg-config python3

dnf config-manager --set-enabled crb
dnf install -y libedit-devel

rpmdev-setuptree
cp SOURCES/* ~/rpmbuild/SOURCES/
cp SPECS/varnish.spec ~/rpmbuild/SPECS/
rpmbuild -ba ~/rpmbuild/SPECS/varnish.spec

RPMs are generated in:

~/rpmbuild/RPMS/x86_64/

The rebuilt Source RPM is instead produced in:

~/rpmbuild/SRPMS/

This allows for a repeatable, versioned, and verifiable process, rather than relying on manual installations or undocumented workarounds.

Testing on EL9: it's not enough that the package compiles

One of the key lessons from this intervention is that, in a legacy rebuild, filling out the form is just the first stepAn RPM can be formally correct, install without errors, and still not be usable in production.

In our case the most important problems emerged only after:

  • the reference to <sys/sysctl.h> it emerged during the compilation phase;
  • the jemalloc bug only surfaced when starting varnishd on EL9;
  • the problem of VCC_CC It only surfaced on a clean host other than the build machine.

For this reason, the testing was performed on minimal EL9 systems, verifying not only the start of the service but also the loading of the VCLs, the HTTP headers, the HIT caches, the behavior under load and the visibility through tools such as varnishstat.

A basic check after startup can be performed with:

curl -I http://localhost:6081/

Expected headers include elements such as:

Via: 1.1 varnish
X-Varnish: ...

In a real environment, of course, this isn't enough. You need to validate application VCLs, backends, bypass rules, cookie management, TTLs, purges, error paths, and all the other specific cases that have been encoded within VCL 3.x logic over the years.

Known limitations of the solution

This solution doesn't transform Varnish 3 into a modern software. It is and remains a legacy component, retained here only for compatibility reasons.

The main limitations are clear:

  • Varnish 3 is End of Life and does not receive upstream security updates;
  • should not be used for new deployments;
  • does not support native TLS;
  • does not support HTTP/2;
  • still uses SysV scripts, managed on EL9 via systemd-sysv-generator;
  • requires gcc at runtime, because the VCL is compiled on the fly to C code.

In production, TLS should be terminated in front of Varnish, for example via nginx, HAProxy, or hitch. For future environments, it will also be worth considering migrating to modern versions of Varnish and completely rewriting the VCLs.

Because this was the right choice for the customer

Theoretically, the simplest answer would have been: "Let's also update Varnish." Operationally, however, it would have been the riskiest choice.

The client didn't just need a new operating system. They needed to maintain application continuity across 43 systems without simultaneously introducing four critical changes:

  • switching from CentOS 7 to RHEL 9;
  • change of runtime and system libraries;
  • Varnish major version change;
  • Complete rewrite of VCLs from version 3.x to version 4 or later.

Separating these issues was the most prudent choice. The operating system was updated first, maintaining the existing cache behavior. Only at a later stage, with a dedicated project, could we evaluate porting the VCLs to Varnish 6 LTS or later versions.

This is an approach we at Managed Server Srl often adopt in complex contexts: avoiding "big bang" migrations when the risks are too high and instead building progressive, verifiable, and reversible paths.

Conclusions

The rebuild of Varnish Cache 3.0.7 for RHEL 9, AlmaLinux 9 and Rocky Linux 9 It was born from a concrete need: to migrate 43 CentOS 7 systems to RHEL 9 without immediately rewriting complex and already validated VCL 3.x in production.

The work was not limited to relaunching a rpmbuild. It was necessary to recover the official Source RPM, adapt the spec to EL9, correct the problem of <sys/sysctl.h>, fix the jemalloc-induced crash, make the VCL runtime compilation command portable, and test the behavior on clean hosts.

The easy part was compiling. The hard part was getting a package that installed, ran, compiled VCLs, and actually ran on EL9, not just on the build machine.

The public repository is available here:

https://github.com/MarcoMarcoaldi/VARNISH3-EL9

It's a compatibility solution, not a recommendation for new projects. But in a real-world context, with legacy systems, critical VCLs, and an infrastructure migration to complete without disrupting production, it was exactly the kind of intervention needed.

Because in real Linux systems, sometimes the most important job isn't installing the latest available version. It's building the technical bridge that allows you to get there without breaking what's currently holding the service up.

Do you have doubts? Don't know where to start? Contact us!

We have all the answers to your questions to help you make the right choice.

Chat with us

Chat directly with our presales support.

0256569681

Contact us by phone during office hours 9:30 - 19:30

Contact us online

Open a request directly in the contact area.

DISCLAIMER, Legal Notes and Copyright. RedHat, Inc. holds the rights to Red Hat®, RHEL®, RedHat Linux®, and CentOS®; AlmaLinux™ is a trademark of the AlmaLinux OS Foundation; Rocky Linux® is a registered trademark of the Rocky Linux Foundation; SUSE® is a registered trademark of SUSE LLC; Canonical Ltd. holds the rights to Ubuntu®; Software in the Public Interest, Inc. holds the rights to Debian®; Linus Torvalds holds the rights to Linux®; FreeBSD® is a registered trademark of The FreeBSD Foundation; NetBSD® is a registered trademark of The NetBSD Foundation; OpenBSD® is a registered trademark of Theo de Raadt; Oracle Corporation holds the rights to Oracle®, MySQL®, MyRocks®, VirtualBox®, and ZFS®; Percona® is a registered trademark of Percona LLC; MariaDB® is a registered trademark of MariaDB Corporation Ab; PostgreSQL® is a registered trademark of PostgreSQL Global Development Group; SQLite® is a registered trademark of Hipp, Wyrick & Company, Inc.; KeyDB® is a registered trademark of EQ Alpha Technology Ltd.; Typesense® is a registered trademark of Typesense Inc.; REDIS® is a registered trademark of Redis Labs Ltd; F5 Networks, Inc. owns the rights to NGINX® and NGINX Plus®; Varnish® is a registered trademark of Varnish Software AB; HAProxy® is a registered trademark of HAProxy Technologies LLC; Traefik® is a registered trademark of Traefik Labs; Envoy® is a registered trademark of CNCF; Adobe Inc. owns the rights to Magento®; PrestaShop® is a registered trademark of PrestaShop SA; OpenCart® is a registered trademark of OpenCart Limited; Automattic Inc. holds the rights to WordPress®, WooCommerce®, and JetPack®; Open Source Matters, Inc. owns the rights to Joomla®; Dries Buytaert owns the rights to Drupal®; Shopify® is a registered trademark of Shopify Inc.; BigCommerce® is a registered trademark of BigCommerce Pty. Ltd.; TYPO3® is a registered trademark of the TYPO3 Association; Ghost® is a registered trademark of the Ghost Foundation; Amazon Web Services, Inc. owns the rights to AWS® and Amazon SES®; Google LLC owns the rights to Google Cloud™, Chrome™, and Google Kubernetes Engine™; Alibaba Cloud® is a registered trademark of Alibaba Group Holding Limited; DigitalOcean® is a registered trademark of DigitalOcean, LLC; Linode® is a registered trademark of Linode, LLC; Vultr® is a registered trademark of The Constant Company, LLC; Akamai® is a registered trademark of Akamai Technologies, Inc.; Fastly® is a registered trademark of Fastly, Inc.; Let's Encrypt® is a registered trademark of the Internet Security Research Group; Microsoft Corporation owns the rights to Microsoft®, Azure®, Windows®, Office®, and Internet Explorer®; Mozilla Foundation owns the rights to Firefox®; Apache® is a registered trademark of The Apache Software Foundation; Apache Tomcat® is a registered trademark of The Apache Software Foundation; PHP® is a registered trademark of the PHP Group; Docker® is a registered trademark of Docker, Inc.; Kubernetes® is a registered trademark of The Linux Foundation; OpenShift® is a registered trademark of Red Hat, Inc.; Podman® is a registered trademark of Red Hat, Inc.; Proxmox® is a registered trademark of Proxmox Server Solutions GmbH; VMware® is a registered trademark of Broadcom Inc.; CloudFlare® is a registered trademark of Cloudflare, Inc.; NETSCOUT® is a registered trademark of NETSCOUT Systems Inc.; ElasticSearch®, LogStash®, and Kibana® are registered trademarks of Elastic NV; Grafana® is a registered trademark of Grafana Labs; Prometheus® is a registered trademark of The Linux Foundation; Zabbix® is a registered trademark of Zabbix LLC; Datadog® is a registered trademark of Datadog, Inc.; Ceph® is a registered trademark of Red Hat, Inc.; MinIO® is a registered trademark of MinIO, Inc.; Mailgun® is a registered trademark of Mailgun Technologies, Inc.; SendGrid® is a registered trademark of Twilio Inc.; Postmark® is a registered trademark of ActiveCampaign, LLC; cPanel®, LLC owns the rights to cPanel®; Plesk® is a registered trademark of Plesk International GmbH; Hetzner® is a registered trademark of Hetzner Online GmbH; OVHcloud® is a registered trademark of OVH Groupe SAS; Terraform® is a registered trademark of HashiCorp, Inc.; Ansible® is a registered trademark of Red Hat, Inc.; cURL® is a registered trademark of Daniel Stenberg; Facebook®, Inc. owns the rights to Facebook®, Messenger® and Instagram®. This site is not affiliated with, sponsored by, or otherwise associated with any of the above-mentioned entities and does not represent any of these entities in any way. All rights to the brands and product names mentioned are the property of their respective copyright holders. All other trademarks mentioned are the property of their respective registrants. MANAGED SERVER® is a European registered trademark of MANAGED SERVER SRL, with registered office in Via Flavio Gioia, 6, 62012 Civitanova Marche (MC), Italy and operational headquarters in Via Enzo Ferrari, 9, 62012 Civitanova Marche (MC), Italy.

JUST A MOMENT !

Have you ever wondered if your hosting sucks?

Find out now if your hosting provider is hurting you with a slow website worthy of 1990! Instant results.

Close the CTA
Back to top