Varnish Cache Plus

Dynamic backends (goto)

Dynamic backends in Varnish allow backends to be defined on request time instead of being predefined. For example, any request header can be used as the source for creating a backend definition.

A clear advantage of this is the possibility of DNS-backed server pool that scale up and down to accommodate the load. This is a great feature that fits well with autoscaling cloud deployments.

vmod-goto integrates with VCL to enable dynamic routing on a per-request basis.

This API applied to Varnish Cache Plus 4.1.

Goto 6.0 API

VCL example

goto behaves like vmod-directors: you first create a director object in vcl_ini that you can then use in vcl_backend_fetch. In this case, we create a pool of all the machines answering to the domain:

import goto;

sub vcl_init {
	new apipool = goto.dns_director("");

sub vcl_backend_fetch {
	set bereq.backend = apipool.backend();

You can also very easily create an HTTP proxy by resolving the host header to a backend, without creating the director (ie. without knowing the hostname before hand):

    import goto;
    sub vcl_backend_fetch {
            set bereq.backend = goto.dns_backend(;



goto.dns_director(STRING s, ...)

Produce a director object, a pool of machines listed by the DNS. This pool is regularly updated (every 10 seconds by default) without blocking.

You can query a backend from this director using the .backend() method. Internally, the director uses round-robin, so every time it’s used, the returned backend will change.

goto.dns_backend(STRING s, ...)

Note: It can only be called from backent-side VCL sub-routines. Calling it from anywhere else won’t return a backend.

While the dns() function solves the problem of resolving a known hostname for long-term use, sometimes an ephemeral backend is needed. This is the goal of this function.

It works the same way as dns() except that instead of a director object, it directly returns a backend to be used immediately. If multiple IPs were returned by the DNS, calling dns_backend() multiple times with the same arguments will cycle through them.


Such backends are cached for at least a ttl period, and can be reused if dns_backend() is called with the same arguments. This avoids making a DNS resolution per call, enabling better performance.

Once a ttl period has elapsed, two things can occur:

  • if the backend wasn’t used for this period, it is marked for death and will be destroyed as soon as it’s not used anymore. It is why .dns_backend() isn’t allowed in vcl_recv() for example: by the time it’s actually used in vcl_fetch, the backend structure may already have been destroyed.
  • if it was used, s is asynchronously resolved again and the backends stay cached for another ttl period.

Optional arguments

Both functions have the same arguments, and all but the first one are optional. Most of them are simply the same as standard backend definitions, defaults are between parenthesis:

  • STRING s: the only mandatory argument, used for the DNS resolution.
  • STRING port (none): if s didn’t specify the port, use this one. If empty too, infer it from the protocol (HTTP->80, HTTPS->443)
  • STRING host_header (none): host header to send to the backend if the bereq doesn’t have one. This is also used for TLS verification.
  • DURATION connect_timeout (none): timeout for connecting to the backends.
  • DURATION first_byte_timeout (none): timeout for the reception of the backend response.
  • DURATION between_bytes_timeout (none): timeout for receiving two consecutive bytes.
  • PROBE probe (none): probe to attach to the resolved backends.
  • INT max_connections (none): number of connections to a backend before returning 503.
  • BOOL ssl (false): enable HTTPS. If s starts with “http://” or “https://“, this is ignored
  • BOOL ssl_sni (true): if using HTTPS, enable Server Name Indication extension for backend connections.
  • BOOL ssl_verify_peer (true): enable verification of the peer’s certification chain.
  • BOOL ssl_verify_host (false): enable verification of the peer’s certificate identity.
  • ENUM ip_version [all, ipv4, ipv6] (all): restrict the IP version of resolved backend. If your DNS returns IPv6 results, make sure you have IPv6 connectivity. Note that the parameter isn’t between quotes, eg. goto.dns_director("", ip_version = ipv6)
  • DURATION ttl (10s): how long should goto wait before revalidating backends.

Deprecated functions

goto.backend() and goto.directors() are now deprecated and will be removed in the next major version. They are documented via man vmod_goto for the VCP version still including them.

The arguments are mostly the same but new API functions use a DURATION for the ttl parameter instead of the old INT. Also, the IP version is a parameter instead of being integrated to the function name. For example:

    goto.backend4("", ttl = 2);
# is now
    goto.dns_backend("", ip_version = ipv4, ttl = 2s);

The same holds true for the .director() functions, and in addition, the new .dns_director() now returns an object that you can reuse like this:

sub vcl_init {
	# create two directors, one round-robin and one fallback
	new fb = directors.fallback();
	new rr = directors.round_robin();

	# create a dns director
	new dns = goto.dns_director("", ttl = 2s);

	# add the dns director to the first two ones


Dynamic backend generation is available starting from Varnish Cache Plus 4.1.4r2.


vmod-goto is prebuilt for supported versions and included inside the varnish-plus package.

The package contains further usage instructions, accessible using man vmod_goto.

Contact if you need assistance.