Varnish Cache

Varnish Cache is a high-performance HTTP accelerator designed for content-heavy websites and applications. By serving cached versions of web pages and content, Varnish significantly reduces load times and alleviates pressure on backend servers, ensuring a seamless user experience. This document provides an in-depth look at Varnish Cache, its architecture, core features, use cases, and best practices for implementation.


Key Features

  1. High Performance Varnish Cache is designed to handle millions of requests per second, making it ideal for scenarios where low latency and high throughput are essential. By offloading traffic from backend servers, it ensures quick response times even during peak loads.

  2. Varnish Configuration Language (VCL) Varnish employs a powerful domain-specific language, VCL, to define how requests and responses are processed. This allows fine-grained control over caching rules, routing, and request handling.

  3. Flexible Caching Policies

    With VCL, developers can implement sophisticated caching strategies, including:

    • Time-to-Live (TTL) settings for specific content.
    • Conditional caching based on headers, cookies, or request parameters.
    • Partial caching for dynamic or personalized content.
  4. Edge Side Includes (ESI) Varnish supports ESI, a markup language that enables dynamic assembly of cached and non-cached content. This is particularly useful for websites requiring personalization or frequently updated data.

  5. Load Balancing Varnish can distribute incoming traffic across multiple backend servers, ensuring high availability and scalability. Load balancing methods include round-robin, random, and least connections.

  6. Logging and Debugging Tools like varnishlog and varnishstat provide real-time insights into Varnish’s operations, helping developers monitor performance and troubleshoot issues effectively.


Architecture

Varnish Cache operates as a reverse proxy, intercepting HTTP requests from clients before they reach the origin server. Its architecture consists of the following components:

  1. Frontend (Client)

    • Users send HTTP requests to the frontend, which are intercepted by Varnish.
  2. Varnish Cache Layer

    • Requests are processed based on VCL rules.
    • Cached responses are served directly, bypassing the backend.
  3. Backend Servers

    • Uncached requests are forwarded to the origin servers.
    • Responses from the backend are stored in the cache for future use.

This architecture ensures minimal latency and optimized resource usage.

Flow

graph LR
    A[Client] --> B[Varnish Cache Layer]
    B -->|Cache Hit| C[Cache]
    B -->|Cache Miss| D[Backend Servers]
    D --> E[Cache Store]
    E --> B

VCL State Machine

The Varnish Configuration Language (VCL) allows granular control over request processing. The (over simplified) state machine for VCL includes the following states:

graph TD
    A[vcl_recv] --> B[vcl_hash]
    B --> C{Cache Hit?}
    C -->|Yes| D[vcl_hit]
    C -->|No| E[vcl_miss]
    E --> F[vcl_pass]
    D --> G[vcl_deliver]
    F --> G
    G --> H[Client]
  • vcl_recv: Initial processing of incoming requests.
  • vcl_hash: Determines the hash for cache lookup.
  • vcl_hit: Executed if a cache hit occurs.
  • vcl_miss: Executed if the object is not in the cache.
  • vcl_pass: Forwards the request to the backend without caching.
  • vcl_deliver: Prepares and sends the response to the client.

Use Cases

  1. Content Delivery for High-Traffic Websites News portals, e-commerce platforms, and video streaming services leverage Varnish Cache to ensure fast content delivery, even during traffic spikes.

  2. APIs and Microservices Varnish can cache API responses, reducing backend latency and improving performance for microservice architectures.

  3. Static Asset Caching Static files like images, CSS, and JavaScript can be cached efficiently, reducing bandwidth consumption and server load.


Best Practices for Implementation

  1. Optimize Caching Policies

    Use VCL to define precise caching rules that align with your application’s requirements. For example:

    • Exclude sensitive data or user-specific content from being cached.
    • Cache large assets for extended durations to reduce backend queries.
  2. Monitor Cache Performance

    Regularly analyze Varnish metrics using tools like varnishstat to identify bottlenecks or inefficiencies in caching.

  3. Implement Grace Mode

    Enable grace mode to serve stale content temporarily when backend servers are unavailable, ensuring consistent user experience.

  4. Leverage Backend Health Checks

    Configure health probes to monitor backend server availability and route traffic to healthy instances.

  5. Scale Horizontally

    For high-traffic environments, deploy multiple Varnish instances with a load balancer to ensure scalability and fault tolerance.


Conclusion

Varnish Cache is a powerful tool for optimizing web performance, offering unmatched speed, flexibility, and scalability. By implementing Varnish effectively, organizations can improve user experiences, reduce infrastructure costs, and handle high traffic with ease. Whether serving static assets, caching dynamic content, or optimizing API responses, Varnish is an indispensable asset in the modern web ecosystem.

Subsections of Varnish Cache

Ingress controller

Build master Build master Audit Audit Clippy Lint Clippy Lint Docker Pulls Docker Pulls Rust Version Rust Version dependency status dependency status Maintenance Maintenance Artifact Hub Artifact Hub License License

Varnish Ingress controller

Lite implementation of a Varnish Ingress controller.


Table of contents


How does it work

The varnish-ingress-controller watches over Ingress objects in the cluster. The watcher is configured to filter through Ingress objects with the following label:

kubernetes.io/ingress: varnish

and Ingress class name:

spec.ingressClassName: varnish

The spec of the Ingress objects is then translated into Varnish VCL.

The varnish-ingress-controller watches over INIT | ADD | UPDATE | DELETE Ingress events and updates the Varnish VCL accordingly. After a succesfull VCL file update, Varnish will reload its VCL just so it becomes aware of the latest configuration.

Example:

The following Ingress spec:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  generation: 4
  labels:
    kubernetes.io/ingress: varnish
  name: media
  namespace: demo
  resourceVersion: "146788664"
  uid: b386a268-0006-446c-9844-3e004712070f
spec:
  ingressClassName: varnish
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          service:
            name: media-v1-svc
            port:
              number: 80
        path: /foo
        pathType: Prefix
  - host: qux.bar.com
    http:
      paths:
      - backend:
          service:
            name: media-v2-svc
            port:
              number: 80
        path: /qux
        pathType: Exact

yields the following VCL:

vcl 4.1;

import directors;
import std;

backend default none;

backend demo-media-media-v1-svc {
  .host = "media-v1-svc.demo.svc.cluster.local";
  .port = "80";
}
  
backend demo-media-media-v2-svc {
  .host = "media-v2-svc.demo.svc.cluster.local";
  .port = "80";
}
  

sub vcl_recv {
 if (req.http.host == "foo.bar.com" && req.url ~ "^/foo") {
        set req.backend_hint = demo-media-media-v1-svc;
    }
 if (req.http.host == "qux.bar.com" && req.url == "/qux") {
        set req.backend_hint = demo-media-media-v2-svc;
    }
}

Installation and usage

At the time of writing this, the installation is available only via Helm from your local machine. Make sure you’re connected to a Kubernetes cluster and run the following:

$ helm package chart/
$ helm upgrade varnish-ingress-controller --install --namespace vingress --create-namespace ./varnish-ingress-controller-0.3.2.tgz -f charts/values.yaml

Update the spec of your Ingress(es) with the following requirements:

  1. add the following label: kubernetes.io/ingress: varnish
  2. set the ingress class: spec.ingressClassName: varnish

Investigate the logs of the varnish-ingress-controller pod, they should reflect the updates mentioned above on your Ingress object(s):

Example:

$ kubectl -n <your-namespace> logs po/varnish-ingress-controller-xxxxxxxxxx-yyyyy 

A Kubernetes service is available to be used for reaching the Varnish containers. It is up to you whether this service should be used in conjuction with a load-balancer or not. For quick testing and shorter feedback loops it’s easier to just port forward it locally:

Example:

$ kubectl -n vingress port-forward svc/varnish-ingress-service 6081:80
$ curl http://127.1:6081/foo -H "Host: foo.bar.com" -v

More VCL

The varnish-ingress-controller translates the Ingress spec into VCL syntax. However, there’s often the case that the generated VCL needs to be extended to accomodate the various use cases.

Check for the varnish-vcl configmap in the namespace where the varnish-ingress-controller is installed. The Configmap has the following fields which is watched by the ingress-controller:

  • vcl_recv_snippet: snippet added in the vcl_recv subroutine after the backends selection
  • snippet: snippet added after the vcl_rec subroutine

Whenever these 2 mentioned fields in the Configmap are updated - the following happens:

  1. update the generated VCL file
  2. issue a varnishreload command just so Varnish picks up the new updates

Example:

$ kubectl -n vingress get cm/varnish-vcl -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
    meta.helm.sh/release-name: varnish-ingress-controller
    meta.helm.sh/release-namespace: vingress
  labels:
    app.kubernetes.io/managed-by: Helm
  name: varnish-vcl
  namespace: vingress
  resourceVersion: "154768231"
data:
  vcl_recv_snippet: |
    if (! req.backend_hint) {
      return (synth(200, "We get here now!"));
    }
  snippet: |
    sub vcl_backend_response {
      if (beresp.status == 200) {
          set beresp.ttl = 5m; 
      }
      unset beresp.http.Cache-Control;
    }

    sub vcl_deliver {
      if (obj.hits > 0) {
          set resp.http.X-Cache = "HIT"; 
      } else {
          set resp.http.X-Cache = "MISS";
      }
      set resp.http.X-Varnish = "X-Varnish-foo";
    }

Misc

This paragraph highlights some assumptions made in this implementation.

  • Single container pod, the Varnish process is started within the controller code
  • The vcl_recv subroutine is configurable only via editing the vcl.hbs template
  • There is no fancy editing of the VCL file, when either the Ingress objects or the varnish-vcl Configmap changes, then the VCL file is rewritten entirely