Overview #
In this tutorial I’m using Varnish as loadbalancer for 3 Apache webservers. Nginx is used for the TLS termination in front of Varnish.
The following VMs are based on Debian 12 bookworm:
192.168.30.20 # Loadbalancer VM: Nginx TLS termination, Varnish loadbalancer
192.168.30.21 # Apache webserver VM 1
192.168.30.22 # Apache webserver VM 2
192.168.30.23 # Apache webserver VM 3
Prerequisites #
Install & Configure Apache #
Install the Apache webserver on the Apache VMs:
# Install Apache
sudo apt install apache2 -y
# Verify the Apache status
sudo systemctl status apache2
Change the default “index.html” file on the Apache VMs and define a different VM number for each VM:
# Adopt the default HTML file
sudo vi /var/www/html/index.html
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM1,2,3</h1>
</body>
</html>
Varnish Loadbalancer #
Install Varnish #
Install Varnish on the loadbalancer VM:
# Install Varnish
sudo apt install varnish -y
# Verify Varnish status
sudo systemctl status varnish
Varnish VCL Configuration #
# Create a backup of the default configuration
sudo cp /etc/varnish/default.vcl /etc/varnish/default.vcl.bak
# Edit the default configuration
sudo vi /etc/varnish/default.vcl
vcl 4.1;
import directors;
backend server1 {
.host = "192.168.30.21";
.port = "80";
}
backend server2 {
.host = "192.168.30.22";
.port = "80";
}
backend server3 {
.host = "192.168.30.23";
.port = "80";
}
sub vcl_init {
new my_backends = directors.round_robin();
my_backends.add_backend(server1);
my_backends.add_backend(server2);
my_backends.add_backend(server3);
}
sub vcl_recv {
set req.backend_hint = my_backends.backend();
return (pass);
}
Restart Varnish #
# Restart Varnish
sudo systemctl restart varnish
# Verify Varnish status
sudo systemctl status varnish
Verify Varnish Output #
Every time Varnish is curled, it should output another Apache VM:
# Curl the Varnish output
curl localhost:6081
curl localhost:6081
curl localhost:6081
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM1</h1>
</body>
</html>
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM2</h1>
</body>
</html>
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM3</h1>
</body>
</html>
Nginx TLS Termination #
Install Nginx #
Install Nginx on the same loadbalancer VM where Varnish is installed:
# Install Nginx
sudo apt install nginx -y
# Verify the Nginx status
sudo systemctl status nginx
# Install Certbot
sudo apt install certbot -y
Nginx Configuration #
# Copy the default configuration
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/varnish-lb-example.jklug.work
# Edit the new configuration
sudo vi /etc/nginx/sites-available/varnish-lb-example.jklug.work
server {
listen 443 ssl;
server_name varnish-lb-example.jklug.work;
ssl_certificate /etc/letsencrypt/live/varnish-lb-example.jklug.work/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/varnish-lb-example.jklug.work/privkey.pem;
# TLS configurations
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://127.0.0.1:6081; # Varnish port 6081
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
# Disable the default configuration
sudo rm /etc/nginx/sites-enabled/default
# Enable the new configuration
sudo ln -s /etc/nginx/sites-available/varnish-lb-example.jklug.work /etc/nginx/sites-enabled/
Restart Nginx #
# Restart Nginx
sudo systemctl restart nginx
# Verify the Nginx status
sudo systemctl status nginx
DNS Entry #
Since I’m using a Let’s Encrypt wildcard certificate it’s necessary to create a local hosts entry:
# Create a DNS entry for Nginx
192.168.30.20 varnish-lb-example.jklug.work
Verify Nginx Output #
# Curl Nginx
curl https://varnish-lb-example.jklug.work
curl https://varnish-lb-example.jklug.work
curl https://varnish-lb-example.jklug.work
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM3</h1>
</body>
</html>
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM1</h1>
</body>
</html>
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM2</h1>
</body>
</html>
More #
Varinish Systemd Service Unit #
# Edit the default systemd service unit file
sudo vi /lib/systemd/system/varnish.service
[Unit]
Description=Varnish Cache, a high-performance HTTP accelerator
Documentation=https://www.varnish-cache.org/docs/ man:varnishd
[Service]
Type=simple
# Maximum number of open files (for ulimit -n)
LimitNOFILE=131072
# Locked shared memory - should suffice to lock the shared memory log
# (varnishd -l argument)
# Default log size is 80MB vsl + 1M vsm + header -> 82MB
# unit is bytes
LimitMEMLOCK=85983232
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
-
-a :6081
Varnish is listening on all available interfaces (0.0.0.0) on port 6081 -
localhost:6082
This interface allows to issue management commands to Varnish, such as purging the cache & loading new VCL configurations -
-f /etc/varnish/default.vcl
Default VCL configuration file
Change Varnish Port #
For example set the default listening port to “8080”:
[Unit]
Description=Varnish Cache, a high-performance HTTP accelerator
Documentation=https://www.varnish-cache.org/docs/ man:varnishd
[Service]
Type=simple
# Maximum number of open files (for ulimit -n)
LimitNOFILE=131072
# Locked shared memory - should suffice to lock the shared memory log
# (varnishd -l argument)
# Default log size is 80MB vsl + 1M vsm + header -> 82MB
# unit is bytes
LimitMEMLOCK=85983232
ExecStart=/usr/sbin/varnishd \
-j unix,user=vcache \
-F \
-a :8080 \
-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
# Reload the systemd configuration
sudo systemctl daemon-reload
# Restart Varnish
sudo systemctl restart varnish
Load new VCL Configuration #
Create New VCL Configuration #
# Create a new VCL configuration
sudo vi /etc/varnish/lb-example.vcl
vcl 4.1;
import directors;
backend server1 {
.host = "192.168.30.21";
.port = "80";
}
backend server2 {
.host = "192.168.30.22";
.port = "80";
}
sub vcl_init {
new my_backends = directors.round_robin();
my_backends.add_backend(server1);
my_backends.add_backend(server2);
}
sub vcl_recv {
set req.backend_hint = my_backends.backend();
return (pass);
}
Load & Activate new VCL Configuration #
Verify the Varnish secret:
# This file is used for authenticating administrative commands
sudo cat /etc/varnish/secret
# Load new VCL configuration (Define path to Varnish secret)
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.load lb-example /etc/varnish/lb-example.vcl
# Activate the new configuration
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.use lb-example
# Shell output:
VCL 'lb-example' now active
List Active & Available VCL Configuartions #
# List active & available configurations
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.list
# Shell output:
available auto warm 0 boot
active auto warm 0 lb-example
Verify Varnish Output: #
Verify the new Varnish configuration, it now only outputs the Apache VM1 & VM2 webservers:
# Curl the Varnish Output
curl localhost:8080
curl localhost:8080
curl localhost:8080
...
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM2</h1>
</body>
</html>
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>jklug.work</title>
</head>
<body>
<h1>Apache VM1</h1>
</body>
</html>
Switch to Default VCL Configuration #
# Switch to default configuration
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.use boot
# Shell output:
VCL 'boot' now active
# List active & available configurations
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.list
# Shell output:
active auto warm 0 boot
available auto warm 0 lb-example
Delete the new VCL Configuration #
# Delete the "lb-example" configuration
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.discard lb-example
# List active & available configurations
sudo varnishadm -T localhost:6082 -S /etc/varnish/secret vcl.list
# Shell output:
active auto warm 0 boot
Links #
# Varnish Official Documentation
https://varnish-cache.org/docs/index.html