Configure Varnish 7 memory allocation and storage backends for optimal cache performance. Set up file-based persistence and tune memory parameters for high-traffic workloads with monitoring.
Prerequisites
- Varnish 7 installed and running
- Root or sudo access
- Basic understanding of web caching concepts
- At least 4GB available disk space for cache storage
What this solves
Varnish 7 ships with default memory settings that work for basic setups but need optimization for production workloads. This tutorial shows you how to configure memory allocation, implement file-based cache persistence, and tune storage backends for high-traffic websites. You'll optimize cache storage to survive restarts and handle memory pressure efficiently.
Step-by-step configuration
Check current Varnish configuration
First, examine your current Varnish setup to understand the baseline configuration and identify optimization opportunities.
sudo systemctl status varnish
sudo varnishstat -1 | grep -E "cache_hit|cache_miss|n_object|g_bytes"
varnishd -V
Stop Varnish for configuration changes
Stop the service to make storage backend changes that require a restart.
sudo systemctl stop varnish
Configure memory allocation and storage backends
Edit the Varnish systemd service file to configure storage backends and memory allocation. The malloc storage type stores cache in memory while file storage provides persistence.
sudo mkdir -p /etc/systemd/system/varnish.service.d
sudo tee /etc/systemd/system/varnish.service.d/override.conf > /dev/null << 'EOF'
[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd \
-a :6081 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-s malloc,2G \
-s file,/var/cache/varnish/storage.bin,4G \
-p workspace_backend=128k \
-p workspace_client=128k \
-p http_max_hdr=128 \
-p thread_pools=4 \
-p thread_pool_min=100 \
-p thread_pool_max=1000
EOF
Create cache storage directory
Create the directory for file-based cache storage with proper ownership and permissions.
sudo mkdir -p /var/cache/varnish
sudo chown varnish:varnish /var/cache/varnish
sudo chmod 755 /var/cache/varnish
Configure advanced memory tuning parameters
Create a VCL configuration file with optimized memory and cache settings for high-traffic workloads.
vcl 4.1;
import std;
import directors;
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 5s;
.first_byte_timeout = 10s;
.between_bytes_timeout = 5s;
.max_connections = 300;
}
sub vcl_recv {
# Memory optimization: Remove cookies for static assets
if (req.url ~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$") {
unset req.http.Cookie;
}
# Memory optimization: Normalize Accept-Encoding
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
unset req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
unset req.http.Accept-Encoding;
}
}
}
sub vcl_backend_response {
# Cache static assets for 1 day
if (bereq.url ~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$") {
set beresp.ttl = 1d;
set beresp.http.Cache-Control = "public, max-age=86400";
}
# Remove server headers that waste cache memory
unset beresp.http.Server;
unset beresp.http.X-Powered-By;
unset beresp.http.X-Drupal-Dynamic-Cache;
# Enable grace mode for better performance
set beresp.grace = 6h;
}
sub vcl_deliver {
# Add cache hit information
if (obj.hits > 0) {
set resp.http.X-Varnish-Cache = "HIT";
} else {
set resp.http.X-Varnish-Cache = "MISS";
}
# Remove internal headers
unset resp.http.Via;
unset resp.http.X-Varnish;
}
Update Varnish to use the optimized configuration
Modify the systemd override to use the memory-optimized VCL file.
sudo tee /etc/systemd/system/varnish.service.d/override.conf > /dev/null << 'EOF'
[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd \
-a :6081 \
-T localhost:6082 \
-f /etc/varnish/memory-optimized.vcl \
-s malloc,2G \
-s file,/var/cache/varnish/storage.bin,4G \
-p workspace_backend=128k \
-p workspace_client=128k \
-p http_max_hdr=128 \
-p thread_pools=4 \
-p thread_pool_min=100 \
-p thread_pool_max=1000 \
-p obj_readonly=on \
-p vcc_allow_inline_c=on \
-p shortlived=10 \
-p lru_interval=20
EOF
Configure cache persistence and storage optimization
Create a script to manage cache persistence across restarts and optimize storage allocation.
#!/bin/bash
Varnish Cache Management Script
CACHE_DIR="/var/cache/varnish"
STORAGE_FILE="${CACHE_DIR}/storage.bin"
LOG_FILE="/var/log/varnish/cache-manager.log"
Create log directory
mkdir -p /var/log/varnish
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
Function to check cache storage health
check_storage_health() {
if [[ -f "$STORAGE_FILE" ]]; then
local file_size=$(stat -c%s "$STORAGE_FILE" 2>/dev/null)
if [[ $file_size -gt 0 ]]; then
log_message "Cache storage file exists and is healthy ($file_size bytes)"
return 0
fi
fi
log_message "Cache storage file missing or corrupted"
return 1
}
Function to initialize storage
init_storage() {
log_message "Initializing cache storage directory"
mkdir -p "$CACHE_DIR"
chown varnish:varnish "$CACHE_DIR"
chmod 755 "$CACHE_DIR"
# Pre-allocate storage file for better performance
if [[ ! -f "$STORAGE_FILE" ]]; then
log_message "Pre-allocating storage file"
fallocate -l 4G "$STORAGE_FILE" 2>/dev/null || dd if=/dev/zero of="$STORAGE_FILE" bs=1M count=4096 2>/dev/null
chown varnish:varnish "$STORAGE_FILE"
chmod 644 "$STORAGE_FILE"
fi
}
Function to cleanup old cache files
cleanup_cache() {
log_message "Cleaning up old cache files"
find "$CACHE_DIR" -name "*.tmp" -mtime +1 -delete 2>/dev/null
find "$CACHE_DIR" -name "varnish*" -mtime +7 -delete 2>/dev/null
}
Main execution
case "$1" in
"init")
init_storage
;;
"check")
check_storage_health
;;
"cleanup")
cleanup_cache
;;
"status")
echo "Cache directory: $CACHE_DIR"
echo "Storage file: $STORAGE_FILE"
if [[ -f "$STORAGE_FILE" ]]; then
echo "Storage size: $(du -h "$STORAGE_FILE" | cut -f1)"
fi
df -h "$CACHE_DIR"
;;
*)
echo "Usage: $0 {init|check|cleanup|status}"
exit 1
;;
esac
Make the cache manager executable and initialize storage
Set proper permissions and initialize the cache storage system.
sudo chmod +x /usr/local/bin/varnish-cache-manager.sh
sudo /usr/local/bin/varnish-cache-manager.sh init
Create systemd timer for cache maintenance
Set up automated cache maintenance to keep storage optimized.
[Unit]
Description=Varnish Cache Maintenance
After=varnish.service
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/varnish-cache-manager.sh cleanup
ExecStart=/usr/local/bin/varnish-cache-manager.sh check
[Unit]
Description=Run Varnish Cache Maintenance
Requires=varnish-maintenance.service
[Timer]
OnCalendar=daily
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target
Reload systemd and start services
Apply the configuration changes and start Varnish with the optimized settings.
sudo systemctl daemon-reload
sudo systemctl enable varnish-maintenance.timer
sudo systemctl start varnish-maintenance.timer
sudo systemctl start varnish
sudo systemctl enable varnish
Configure memory monitoring script
Create a monitoring script to track Varnish memory usage and cache performance.
#!/bin/bash
Varnish Performance Monitoring Script
MONITOR_LOG="/var/log/varnish/performance.log"
Create log directory
mkdir -p /var/log/varnish
Function to log performance metrics
log_metrics() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# Get Varnish statistics
local cache_hits=$(varnishstat -1 -f cache_hit | awk '{print $2}')
local cache_misses=$(varnishstat -1 -f cache_miss | awk '{print $2}')
local objects=$(varnishstat -1 -f n_object | awk '{print $2}')
local bytes=$(varnishstat -1 -f g_bytes | awk '{print $2}')
local memory_usage=$(varnishstat -1 -f SMA.s0.g_bytes | awk '{print $2}')
# Calculate hit ratio
local total_requests=$((cache_hits + cache_misses))
local hit_ratio=0
if [[ $total_requests -gt 0 ]]; then
hit_ratio=$(echo "scale=2; $cache_hits * 100 / $total_requests" | bc -l)
fi
# Log metrics
echo "$timestamp,hits:$cache_hits,misses:$cache_misses,objects:$objects,bytes:$bytes,memory:$memory_usage,hit_ratio:${hit_ratio}%" >> "$MONITOR_LOG"
# Check for memory pressure
local memory_mb=$((memory_usage / 1024 / 1024))
if [[ $memory_mb -gt 1800 ]]; then
logger -t varnish-monitor "WARNING: Memory usage high: ${memory_mb}MB"
fi
# Check hit ratio
if [[ $(echo "$hit_ratio < 80" | bc -l) -eq 1 ]] && [[ $total_requests -gt 1000 ]]; then
logger -t varnish-monitor "WARNING: Low hit ratio: ${hit_ratio}%"
fi
}
Run monitoring
log_metrics
Keep only last 7 days of logs
find /var/log/varnish -name "performance.log" -mtime +7 -delete 2>/dev/null
Make monitoring script executable and create cron job
Set up regular performance monitoring to track cache efficiency.
sudo chmod +x /usr/local/bin/varnish-monitor.sh
Create cron job to run every 5 minutes
echo "/5 * /usr/local/bin/varnish-monitor.sh" | sudo crontab -
Verify your setup
Check that Varnish is running with the optimized configuration and monitoring the cache performance.
# Check service status
sudo systemctl status varnish
Verify storage configuration
varnishd -C -f /etc/varnish/memory-optimized.vcl
Check memory allocation
varnishstat -1 | grep -E "SMA|cache_hit|cache_miss|n_object"
Test cache functionality
curl -I http://localhost:6081/
Check storage files
sudo /usr/local/bin/varnish-cache-manager.sh status
View performance log
tail -f /var/log/varnish/performance.log
You should see Varnish running with both malloc and file storage backends, cache hits/misses being tracked, and performance metrics being logged.
Storage configuration options
Different storage backends offer various trade-offs between performance and persistence:
| Storage Type | Use Case | Performance | Persistence |
|---|---|---|---|
| malloc | High-performance caching | Fastest | Lost on restart |
| file | Persistent cache storage | Fast | Survives restarts |
| persistent | Long-term storage | Medium | Full persistence |
Memory tuning parameters
Key parameters for optimizing Varnish memory usage:
| Parameter | Purpose | Recommended Value |
|---|---|---|
| workspace_backend | Backend communication buffer | 128k for most workloads |
| workspace_client | Client communication buffer | 128k for most workloads |
| http_max_hdr | Maximum HTTP headers | 128 for typical websites |
| thread_pool_min | Minimum worker threads | 100 for high traffic |
| thread_pool_max | Maximum worker threads | 1000 for high traffic |
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Varnish won't start | Storage file permissions | sudo chown varnish:varnish /var/cache/varnish |
| Cache not persisting | File storage not configured | Check -s file parameter in service config |
| High memory usage | Malloc storage too large | Reduce malloc size, increase file storage |
| Low hit ratio | TTL too short or cookies interfering | Check VCL cookie handling and TTL settings |
| "No space left" error | Storage file fills disk | Move storage to larger partition or reduce size |
| Performance degradation | Too many threads or insufficient workspace | Tune thread pool and workspace parameters |
Next steps
- Monitor Varnish 7 performance with Prometheus and Grafana dashboards
- Configure Varnish ESI (Edge Side Includes) for dynamic content optimization
- Set up Varnish 7 cluster with load balancing across multiple backends
- Configure Varnish SSL termination with NGINX reverse proxy
- Implement Varnish cache warming strategies for production
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration
CACHE_DIR="/var/cache/varnish"
STORAGE_SIZE="${VARNISH_STORAGE_SIZE:-4G}"
MEMORY_SIZE="${VARNISH_MEMORY_SIZE:-2G}"
LISTEN_PORT="${VARNISH_PORT:-80}"
BACKEND_HOST="${BACKEND_HOST:-127.0.0.1:8080}"
# Functions
log() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
exit 1
}
usage() {
echo "Usage: $0 [options]"
echo "Options:"
echo " --storage-size SIZE Cache storage size (default: 4G)"
echo " --memory-size SIZE Memory allocation (default: 2G)"
echo " --port PORT Listen port (default: 80)"
echo " --backend HOST:PORT Backend server (default: 127.0.0.1:8080)"
echo " --help Show this help"
exit 0
}
cleanup() {
if [[ "${VARNISH_STOPPED:-}" == "true" ]]; then
warn "Failure detected, restarting Varnish with original configuration"
systemctl start varnish || true
fi
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--storage-size) STORAGE_SIZE="$2"; shift 2 ;;
--memory-size) MEMORY_SIZE="$2"; shift 2 ;;
--port) LISTEN_PORT="$2"; shift 2 ;;
--backend) BACKEND_HOST="$2"; shift 2 ;;
--help) usage ;;
*) error "Unknown option: $1" ;;
esac
done
# Trap for cleanup on error
trap cleanup ERR
# Check prerequisites
[[ $EUID -eq 0 ]] || error "This script must be run as root"
echo "[1/10] Detecting distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
VARNISH_CONFIG="/etc/varnish/default.vcl"
SYSTEMD_DIR="/etc/systemd/system"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf makecache"
VARNISH_CONFIG="/etc/varnish/default.vcl"
SYSTEMD_DIR="/etc/systemd/system"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum makecache"
VARNISH_CONFIG="/etc/varnish/default.vcl"
SYSTEMD_DIR="/etc/systemd/system"
;;
*)
error "Unsupported distribution: $ID"
;;
esac
log "Detected: $PRETTY_NAME"
else
error "Cannot detect distribution"
fi
echo "[2/10] Updating package cache..."
$PKG_UPDATE > /dev/null 2>&1
echo "[3/10] Installing Varnish 7..."
if ! command -v varnishd &> /dev/null; then
case "$PKG_MGR" in
apt)
$PKG_INSTALL curl gnupg2 ca-certificates lsb-release
curl -fsSL https://packagecloud.io/varnishcache/varnish70/gpgkey | gpg --dearmor > /etc/apt/trusted.gpg.d/varnish.gpg
echo "deb https://packagecloud.io/varnishcache/varnish70/$ID/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/varnishcache_varnish70.list
apt update
$PKG_INSTALL varnish
;;
dnf|yum)
curl -fsSL https://packagecloud.io/varnishcache/varnish70/config_file.repo -o /etc/yum.repos.d/varnishcache_varnish70.repo
$PKG_INSTALL varnish
;;
esac
else
log "Varnish already installed"
fi
echo "[4/10] Creating cache directory structure..."
mkdir -p "$CACHE_DIR"
chown varnish:varnish "$CACHE_DIR"
chmod 755 "$CACHE_DIR"
echo "[5/10] Stopping Varnish for configuration..."
VARNISH_STOPPED="true"
systemctl stop varnish
echo "[6/10] Configuring storage backend and memory allocation..."
mkdir -p "${SYSTEMD_DIR}/varnish.service.d"
cat > "${SYSTEMD_DIR}/varnish.service.d/override.conf" << EOF
[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd \\
-a :${LISTEN_PORT} \\
-T 127.0.0.1:6082 \\
-f ${VARNISH_CONFIG} \\
-s file,${CACHE_DIR}/cache.bin,${STORAGE_SIZE} \\
-s malloc,${MEMORY_SIZE} \\
-p workspace_backend=256k \\
-p workspace_client=256k \\
-p http_max_hdr=128 \\
-p thread_pool_min=50 \\
-p thread_pool_max=5000 \\
-p thread_pools=4 \\
-p session_linger=50 \\
-p feature=+http2
EOF
echo "[7/10] Creating optimized VCL configuration..."
cat > "$VARNISH_CONFIG" << 'EOF'
vcl 4.1;
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 5s;
.first_byte_timeout = 30s;
.between_bytes_timeout = 5s;
.max_connections = 50;
.probe = {
.url = "/";
.timeout = 3s;
.interval = 10s;
.window = 5;
.threshold = 3;
};
}
sub vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return (synth(405, "Method not allowed"));
}
return (purge);
}
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
if (req.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico|woff|woff2)$") {
unset req.http.Cookie;
set req.ttl = 1h;
}
if (req.http.Cache-Control ~ "no-cache") {
return (pass);
}
return (hash);
}
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
return (lookup);
}
sub vcl_backend_response {
if (beresp.status == 200 || beresp.status == 301 || beresp.status == 302) {
if (bereq.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico|woff|woff2)$") {
unset beresp.http.Set-Cookie;
set beresp.ttl = 24h;
set beresp.http.Cache-Control = "public, max-age=86400";
} else {
set beresp.ttl = 2m;
}
}
if (beresp.ttl <= 0s) {
set beresp.ttl = 120s;
set beresp.uncacheable = true;
return (deliver);
}
return (deliver);
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Varnish-Cache = "HIT";
} else {
set resp.http.X-Varnish-Cache = "MISS";
}
unset resp.http.Via;
unset resp.http.X-Varnish;
}
acl purge {
"localhost";
"127.0.0.1";
"::1";
}
EOF
# Update backend in VCL if custom backend provided
if [[ "$BACKEND_HOST" != "127.0.0.1:8080" ]]; then
IFS=':' read -r BACKEND_IP BACKEND_PORT <<< "$BACKEND_HOST"
sed -i "s/127.0.0.1/$BACKEND_IP/" "$VARNISH_CONFIG"
sed -i "s/8080/$BACKEND_PORT/" "$VARNISH_CONFIG"
fi
echo "[8/10] Creating cache management script..."
cat > /usr/local/bin/varnish-cache-manager.sh << 'EOF'
#!/bin/bash
CACHE_DIR="/var/cache/varnish"
STORAGE_FILE="$CACHE_DIR/cache.bin"
case "$1" in
init)
mkdir -p "$CACHE_DIR"
chown varnish:varnish "$CACHE_DIR"
chmod 755 "$CACHE_DIR"
;;
cleanup)
find "$CACHE_DIR" -name "*.tmp" -mtime +1 -delete 2>/dev/null || true
;;
status)
echo "Cache directory: $CACHE_DIR"
if [[ -f "$STORAGE_FILE" ]]; then
echo "Storage size: $(du -h "$STORAGE_FILE" 2>/dev/null | cut -f1 || echo 'N/A')"
fi
df -h "$CACHE_DIR" 2>/dev/null || echo "Directory not accessible"
;;
*)
echo "Usage: $0 {init|cleanup|status}"
exit 1
;;
esac
EOF
chmod 755 /usr/local/bin/varnish-cache-manager.sh
/usr/local/bin/varnish-cache-manager.sh init
echo "[9/10] Setting up systemd timer for maintenance..."
cat > /etc/systemd/system/varnish-maintenance.service << EOF
[Unit]
Description=Varnish Cache Maintenance
After=varnish.service
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/varnish-cache-manager.sh cleanup
EOF
cat > /etc/systemd/system/varnish-maintenance.timer << EOF
[Unit]
Description=Run Varnish Cache Maintenance
Requires=varnish-maintenance.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable varnish-maintenance.timer
systemctl start varnish-maintenance.timer
echo "[10/10] Starting and enabling Varnish..."
systemctl daemon-reload
systemctl enable varnish
systemctl start varnish
VARNISH_STOPPED="false"
# Verification
sleep 3
if systemctl is-active --quiet varnish; then
log "✓ Varnish is running successfully"
log "✓ Storage backend configured: file,${CACHE_DIR}/cache.bin,${STORAGE_SIZE}"
log "✓ Memory allocation: malloc,${MEMORY_SIZE}"
log "✓ Listening on port: ${LISTEN_PORT}"
log "✓ Backend configured: ${BACKEND_HOST}"
# Show statistics
if command -v varnishstat &> /dev/null; then
echo -e "\n${GREEN}Current Varnish Statistics:${NC}"
varnishstat -1 | grep -E "cache_hit|cache_miss|n_object|g_bytes" || true
fi
else
error "Varnish failed to start. Check logs with: journalctl -u varnish"
fi
log "Varnish 7 optimization complete!"
log "Management script: /usr/local/bin/varnish-cache-manager.sh"
log "View status: /usr/local/bin/varnish-cache-manager.sh status"
Review the script before running. Execute with: bash install.sh