Benchmarking Magento Part 4 - Varnish Cache

Varnish is an in-memory caching proxy server that caches website objects such as images, html pages, CSS files in RAM. Varnish works by accepting the web requests before Apache and serving an object very rapidly if it has it in its cache. If Varnish does not have the object then it will pass on the request to Apache.

This post is Part 4 of the Magento benchmarking series of posts. The server I am benchmarking here is the standard server used in Part 1 with PHP-FPM installed and configured from Part 2. You can see those posts here along with the Reddis cached server in Part 3:

The Varnish cache will replace the Reddis cache as they cannot be both run at the same time on the same server with Magento.

Installing Varnish

Varnish is available in the default Debian repositories so a simple apt install will get it on your server:

apt update
apt upgrade
apt install varnish

The default configuration for Varnish is as follows:

The interface needs to be changed to make Varnish only listen on localhost (127.0.0.1). Also, the cache size is quite large given that our system only has 1GB so this is reduced to 128MB.

We are going to modify the systemd startup configuration for Varnish by creating a systemd override file rather than editing the service file directly. This allows us to modify the startup behavior in a way that will be preserved across updates to the service file.

First, create the directory that will contain the override file:

mkdir /etc/systemd/system/varnish.service.d/

Next, we need to identify the line that we need to change in the service file. The easiest way to do this is to use the systemctl cat <APPLICATION>.service command:

systemctl cat varnish.service
# /lib/systemd/system/varnish.service
[Unit]
Description=Varnish HTTP accelerator
Documentation=https://www.varnish-cache.org/docs/6.1/ man:varnishd

[Service]
Type=simple
LimitNOFILE=131072
LimitMEMLOCK=82000
ExecStart=/usr/sbin/varnishd -j unix,user=vcache -F -a :6081 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m
ExecReload=/usr/share/varnish/varnishreload
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
PrivateDevices=true

[Install]
WantedBy=multi-user.target

The only line we need to change is the line beginning ExecStart as it sets the cache size with the -s malloc,256m directive and the interface with -a localhost:6081. Open a new file at /etc/systemd/system/varnish.service.d/override.conf with the following contents:

[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd -j unix,user=vcache -F -a localhost:6081 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,128m

Note, the first, empty ExecStart= is necessary so that the line from the original service file is removed and the new one applied.

Systemd will now substitute the modified ExecStart line in the override.conf file for the line in the service file but preserve all the other lines.

The following command will instruct systemd to load the new configuration:

systemctl daemon-reload

Restart Varnish to pick up its new configuration:

systemctl restart varnish.service

Use the same process to modify the interface and/or the port if you need to do that.

Configuring Apache2

We cannot set Varnish to listen for the incoming HTTPS connections because it does not have the ability to terminal HTTPS connections. Instead, we will use an additional virtual host file for Apache2 to accept the inbound HTTPS connection then pass the connections off to Varnish listening on a private IP/port which in turn will pass them to another Apache2 VirtualHost also listening on an internal IP/port which will finally serve the Magento site content.

This setup is probably best demonstrated with a diagram:

The process to get this working will be:

  1. Modify the existing VirtualHost to terminate the HTTPS requests and pass them on to Varnish.
  2. Configure Varnish to accept the HTTP requests and hand off any it can’t fulfill to the internal Apache process.

We will also need to enable a couple of additional Apache modules to get the proxy redirection working. The first is the xforward mod:

apt install libapache2-mod-xforward

Then, enable the headers module:

a2enmod headers

And restart Apache:

systemctl restart apache2.service

Modify Magento’s existing VirtualHost file

I am going to use a standard HTTPS VirtualHost file that was generated by Let’s Encrypt. Here it is in full:

<IfModule mod_ssl.c>
<VirtualHost *:443>

        ServerName <MAGENTO HOSTNAME>
        DocumentRoot <MAGENTO DOCUMENTROOT>
        ErrorLog ${APACHE_LOG_DIR}/magento_error.log
        CustomLog ${APACHE_LOG_DIR}/magento_access.log combined

        <Directory <MAGENTO DOCUMENTROOT> >
                Options FollowSymLinks
                AllowOverride All
        </Directory>

        SSLCertificateFile /etc/letsencrypt/live/<MAGENTO HOSTNAME>/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/<MAGENTO HOSTNAME>/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf

</VirtualHost>
</IfModule>

We will need to remove the <Directory> section and the <DocumentRoot line as this will get moved to the new VirtualHost that we are going to create that will actually serve the site.

In their place we need to add some lines to instruct Apache to pass on the requests i.e. proxy them, to another server i.e. Varnish. Varnish is listening on localhost:6081 (or 127.0.0.1:6081) so the new VirtualHost looks like this:

<IfModule mod_ssl.c>
<VirtualHost *:443>

        ServerName <MAGENTO HOSTNAME>
        ErrorLog ${APACHE_LOG_DIR}/magento_external_HTTPS_error.log
        CustomLog ${APACHE_LOG_DIR}/magento_external_HTTPS_access.log combined

        ProxyPreserveHost   On
        ProxyPass           / http://127.0.0.1:6081/
        ProxyPassReverse    / http://127.0.0.1:6081/
        RequestHeader       set X-Forwarded-Proto expr=%{REQUEST_SCHEME}

        SSLCertificateFile /etc/letsencrypt/live/<MAGENTO HOSTNAME>/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/<MAGENTO HOSTNAME>/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf

</VirtualHost>
</IfModule>

Apache is now accepting and terminating the HTTPS sessions and passing them on the Varnish. We now need to create the second Apache VirtualHost on the “other side” of Varnish that will serve the site by fulfilling non-cached requests.

Configure a new internal VirtualHost

This VirtualHost file will instruct Apache to listen on localhost:8080 for HTTP requests for the Magento site. These requests will arrive from Varnish for any non-cached content.

The VirtualHost file need the following contents:

<VirtualHost 127.0.0.1:8080>

        ServerName <MAGENTO HOSTNAME>
        DocumentRoot <MAGENTO DOCUMENTROOT>

        ErrorLog ${APACHE_LOG_DIR}/magento_internal_HTTP_error.log
        CustomLog ${APACHE_LOG_DIR}/magento_internal_HTTP_access.log combined

        SetEnvIf X-Forwarded-Proto https HTTPS=on
        RemoteIPHeader X-Forwarded-For

        <Directory <MAGENTO DOCUMENTROOT> >
                Options FollowSymLinks
                AllowOverride All
        </Directory>

</VirtualHost>

Apache will not listen on 127.0.0.1:8080 unless it is specified in the /etc/apache2/ports.conf file. Open this file add the following line:

Listen 127.0.0.1:8080

Enable the new VirtualHost and restart Apache to load all the new configuration.

Your Magento site should be online and working now without any errors.

Magento and Varnish need some additional tuning configuration to complete the work.

Configure Magento

Magento needs to be configured to know about the Varnish cache. It will also generate some highly optimized Varnish configuration that we will configure Varnish to use in the next section.

Log into the Admin pages for Magento and browse to:

Magento -> Stores -> Configuration -> Advanced -> System

Expand the Full Page Cache section. Un check the box marked Use System Value on the Caching Application line. The drop-down list will become available. Select Varnish Cache (Recommended).

Next expand the Varnish Configuration section immediately below. We need to generate the VLC configuration that is appropriate for the version of Varnish installed on the server. In the case of Debian 10 Buster this is Varnish 6.

Clicking this button will prompt a file download. This is a text file that contains Varnish configuration. Save and open this file with a text editor for the next section.

Finally, hit Save Config to retain the configuration.

Configure Varnish

Back on the server’s command line move to the central Varnish configuration directory at /etc/varnish.

Rename the existing Varnish configuration file:

mv default.vcl default.vcl.original

Next, open a new configuration file:

nano default.vlc

and copy and past the contents of the file you downloaded from the Magento admin panel. Then reload Varnish:

systemctl restart varnish.service

Your Magento instance should now be getting served via the Magento cache.

Benchmarks Results

The following tables shows the results of adding a Redis cache to the basic server also running PHP-FPM:

HTTP HTTPS
Stock server 147 trans/sec 111 trans/sec
PHP-FPM 165 trans/sec (12%) 116 trans/sec (5%)
Redis & PHP-FPM 187 trans/sec (27%) 142 trans/sec (28%)
Varnish PHP-FPM 1616 trans/sec (999%) 299 trans/sec (168%)

I wasn’t sure that the HTTP result was correct so I had to check in a browser that would allow me to use both HTTP and HTTPS (this is diabled in Firefox and Chrome) and the Magento instance was indeed being served at that speed. However, this isn’t really much use as only HTTPS sites are acceptable these days.

The final post in this series will be observing the effect of increasing the size of the VM to include more memory and more CPU cores.