Varnish Cache Plus

Unified director object (udo)


This vmod offers flexible load-balancing features, with notably local reconfiguration and smart retry behavior.

vcl 4.1;

import udo;

backend be1 { .host = ""; }
backend be2 { .host = ""; }
backend be3 { .host = ""; }
backend be4 { .host = ""; }

# create a weighted random director
sub vcl_init {
	new udo_dir = udo.director();
	udo_dir.add_backend(s1, weight = 1);
	udo_dir.add_backend(s2, weight = 1);
	udo_dir.add_backend(s3, weight = 1);
	udo_dir.add_backend(s4, weight = 4);

sub vcl_backend_fetch {
	set bereq.backend_hint = udo_dir.backend();

	# for videos, use consistent hashing
	if (bereq.url ~ "^/video/") {

sub vcl_backend_response {
	# if the response isn't a 200, retry, udo_dir will pick a different
	# backend, even with consistent hashin enabled
	if (beresp.status != 200) {
		return (retry);

An important facet of udo is that each task get its own cache of the state, allowing the VCL writer to change load-balancing policy or to blocklist some backends easily on a per-request fashion.



OBJECT udo.director()

Creates a new director. must be called in vcl_init.


An empty director object, configure for consistent hashing.


Method VOID .add_backend(BACKEND backend, REAL weight = 1, [BLOB hash])

Introduce be into the director, with an optional weight and hash. If no hash is given, the VCL name of the backend is used.

Note that this call must be made from vcl_init and will fail the initialization of the VCL if a backend with the same hash already exists in the director.


  • BACKEND backend - the backend to add to the director
  • REAL weight - a number dictating how much of the traffic should be pushed to the new backend
  • BLOB hash - override the hash of the backend (based on its VCL name). Must be 32 bytes long.




VOID .set_type(ENUM {hash, fallback, random})

Changes the type of the director:

  • hash: use Highest Random Weight algorithm, using the backends and object hashes to pick a backend
  • fallback: use the first healthy backend available, by order of addition in the director
  • random: pick a random backend

If used in vcl_init, the change is global and all requests will inherit this property. If called from client (e.g. vcl_recv) or backend (e.g. vcl_backend_fetch) context, the type is changed just within this context.


  • ENUM {hash, fallback, random} - a policy keyword




VOID .set_hash(BLOB hash)

Override the object hash, within a client or backend context. It’s useful in two notable cases: - to select a backend on a subset of the URL, for example in the video case where you need to send all requests pertaining to one stream to the same backend - to use .dump() before vcl_hash has been run


  • BLOB hash - The object hash to use for the current task




STRING .dump(ENUM {list, json} fmt = list)

Output some of the director’s internal information.

json will produce a fairly complete (and large) string:

  "hash": "0x77d833ebbeae1ef5fcc14eb2fe94cf23a7d12bf5d582926e199b68808c6788b0",
  "type": "hash",
  "backends": [
      "name": "s2",
      "used": false,
      "score": 5.733361,
      "weight": 1.000000,
      "hash": "0xad328846aa18b32a335816374511cac163c704b8c57999e51da9f908290a7a4"

While list is a simple comma-separated list:

s2, s1, s3, s4


  • ENUM {list, json} fmt - the output format to use, defaults to list


The current request-local state of the director in the selected format.


BACKEND .backend()

Return a backend, according to the current type set.




A healthy, non-used backend.


VOID .exhaust_backend(BACKEND be)

Backends are automatically used and avoided later while in the same task, but this function forcefully marks be as “used” so that it won’t be returned again while in the same VCL task, without needing to actually use it first.


  • BACKEND be - The backend to avoid.