Skip to content

varnish/gateway

Repository files navigation

Varnish Gateway

CI codecov

Kubernetes Gateway API implementation using Varnish. A conformant v1.5 implementation for the HTTPRoute profile, covering core and several extended features.

📖 Documentation: gateway.varnish.org

Container Images

Pre-built images are available on GitHub Container Registry:

ghcr.io/varnish/gateway-operator
ghcr.io/varnish/gateway-chaperone

Images are public and require no authentication to pull. Available for linux/amd64 and linux/arm64.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        Kubernetes Cluster                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────────┐         watches          ┌──────────────────┐ │
│   │  Operator   │ ◄────────────────────────│  Gateway API     │ │
│   │             │                          │  Resources       │ │
│   └──────┬──────┘                          │  - Gateway       │ │
│          │                                 │  - HTTPRoute     │ │
│          │ creates/updates                 │  - GatewayClass  │ │
│          ▼                                 └──────────────────┘ │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                    Varnish Pod                          │   │
│   │  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐  │   │
│   │  │  Varnish    │    │  Chaperone  │    │  ConfigMaps │  │   │
│   │  │  + ghost    │◄───│             │◄───│  - main.vcl │  │   │
│   │  │             │    │             │    │             │  │   │
│   │  └──────┬──────┘    └──────┬──────┘    └─────────────┘  │   │
│   │         ▼                  │                            │   │
│   │     varnishlog-json        │ watches                    │   │
│   │                            ▼                            │   │
│   │                     EndpointSlices                      │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Components

Operator - Cluster-wide deployment. Watches Gateway API resources and:

  • Creates Varnish pods with ghost VMOD + chaperone
  • Generates VCL preamble + user VCL (concatenated)
  • Writes routing rules to routing.json (via ConfigMap)

Chaperone - Runs varnishd as a child process in each gateway pod:

  • Watches EndpointSlices, writes backend IPs to ghost.json
  • Triggers ghost reload via HTTP (/.varnish-ghost/reload)
  • Hot-reloads VCL via varnishadm when main.vcl changes

Ghost VMOD - Rust-based routing inside Varnish:

  • Reads ghost.json at init and on reload
  • Matches requests by hostname (exact + wildcard)
  • Weighted backend selection
  • Async HTTP client with connection pooling

Config Reload

Two separate reload paths, both zero-downtime:

  • VCL changes (user VCL updates): varnishadm hot-reload
  • Backend/routing changes: ghost HTTP reload

Logging

Both the operator and chaperone log to stderr at info by default. Set LOG_LEVEL to debug, info, warn, or error (case-insensitive) on either binary to change it; unknown values fall back to info with a warning. On chaperone, debug adds per-event traces for VCL reloads (vcl.load / vcl.use / vcl.discard), varnishadm command/response payloads, ghost reload activity, and endpoint changes — useful when diagnosing reload issues.

LOG_LEVEL set on the operator is propagated into every chaperone pod the operator reconciles, so a single switch on the operator Deployment ripples through the data plane:

kubectl set env deploy/varnish-gateway-operator -n varnish-gateway-system LOG_LEVEL=debug

Newly created or rolled gateway pods will then start with LOG_LEVEL=debug in their env. To flip an already-running gateway pod without restarting the operator, patch it directly: kubectl set env deploy/<gateway-name> -c varnish-gateway LOG_LEVEL=debug. See the reference table for the full list of chaperone environment variables.

Caching

By default, Varnish Gateway operates as a pure reverse proxy with no caching. Every request passes through to the backend.

To enable caching, create a VarnishCachePolicy (VCP) targeting a Gateway, HTTPRoute, or individual route rule. VCP controls TTL, grace/keep, cache key customization, and bypass conditions.

apiVersion: gateway.varnish-software.com/v1alpha1
kind: VarnishCachePolicy
metadata:
  name: cache-static
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: static-assets
  defaultTTL: 1h
  grace: 5m

See VarnishCachePolicy Reference for full documentation.

Real-Time Dashboard

Chaperone includes a real-time web dashboard that shows live gateway state via Server-Sent Events (SSE). It is always enabled on port 9000 inside the chaperone pod. Access it via port-forward:

kubectl port-forward -n namespace deploy/demo-gateway 9000:9000

Then open http://localhost:9000. The dashboard shows:

  • Gateway status (ready/starting/draining) with a live heartbeat trace
  • Virtual hosts and their routes
  • Services with individual backend endpoints
  • Live event stream (ghost reloads, VCL reloads, endpoint changes, TLS updates)

The dashboard updates every second and highlights changes as they happen. It is not exposed outside the cluster unless you explicitly create a Service for it.

Configuration Files

routing.json (operator → ConfigMap):

{
  "version": 2,
  "vhosts": {
    "api.example.com": {
      "routes": [
        {
          "path_match": {"type": "PathPrefix", "value": "/v2"},
          "service": "api-v2",
          "namespace": "default",
          "port": 8080,
          "weight": 100,
          "priority": 10300,
          "rule_index": 0
        }
      ]
    }
  }
}

ghost.json (chaperone → ghost VMOD):

{
  "version": 2,
  "vhosts": {
    "api.example.com": {
      "routes": [
        {
          "path_match": {"type": "PathPrefix", "value": "/v2"},
          "backend_groups": [
            {
              "weight": 100,
              "backends": [
                {"address": "10.0.0.1", "port": 8080},
                {"address": "10.0.0.2", "port": 8080}
              ]
            }
          ],
          "priority": 10300,
          "rule_index": 0
        }
      ]
    }
  }
}

Weights belong to backend groups (services), not individual pods. Selection is two-level: pick a group by weight, then pick a pod within the group at random.

Known Limitations

  • BackendTLSPolicy is currently non-functional. Varnish lacks per-backend CA certificate configuration, so backend TLS verification cannot be implemented correctly. The conformance tests for BackendTLSPolicy are skipped. This will be resolved when varnish/varnish#26 is fixed.

Installation

Helm (Recommended):

helm install varnish-gateway \
  oci://ghcr.io/varnish/charts/varnish-gateway \
  --namespace varnish-gateway-system \
  --create-namespace

kubectl (Alternative):

# Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml

# Deploy the operator
kubectl apply -f deploy/

See docs/getting-started/installation.md for detailed installation instructions and configuration options.

Loading Custom VMODs

The recommended way to load custom VMODs is to build a custom container image that includes them, then point your GatewayClass at it via spec.image:

apiVersion: gateway.varnish-software.com/v1alpha1
kind: GatewayClassParameters
metadata:
  name: custom-varnish
spec:
  image: my-registry/varnish-gateway-custom:v1.2.3

The custom image is typically a one-liner Dockerfile that adds your VMOD to the base image:

FROM ghcr.io/varnish/gateway-chaperone:vX.Y.Z
COPY libvmod_custom.so /usr/lib/varnish/vmods/

This is the most reliable approach: the VMOD is always present when varnishd starts, there are no race conditions, and it works identically in every environment. The logging sidecar (if configured) also inherits the custom image automatically, unless logging.image is set explicitly.

Changing spec.image triggers a rolling restart of all gateway pods using that GatewayClass.

Alternative: Init containers

For cases where building a custom image is not practical, you can use extraInitContainers to copy VMOD files into a shared volume at pod startup:

apiVersion: gateway.varnish-software.com/v1alpha1
kind: GatewayClassParameters
metadata:
  name: varnish-params
spec:
  varnishdExtraArgs:
    - "-p"
    - "vmod_path=/usr/lib/varnish/vmods:/extra-vmods"
  extraVolumes:
    - name: extra-vmods
      emptyDir: {}
  extraVolumeMounts:
    - name: extra-vmods
      mountPath: /extra-vmods
  extraInitContainers:
    - name: install-vmods
      image: my-registry/my-vmods:latest
      command: ["cp", "/vmods/libvmod_custom.so", "/dst/"]
      volumeMounts:
        - name: extra-vmods
          mountPath: /dst

The init container copies the .so files into a shared emptyDir volume, the main container mounts that volume, and varnishdExtraArgs extends the VMOD search path so Varnish finds them. This approach is more fragile than a custom image since it depends on init container ordering during pod startup.

See CLAUDE.md for development setup and detailed documentation.

About

Gateway API implemention for Varnish Cache and Varnish Enterprise

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors