Varnish Enterprise

Dynamic backends (goto)

Varnish 6.0


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. This vmod can currently create and update directors based on:

  • CNAME records
  • SRV records
  • AAAA (IPv6) addresses
  • A (IPv4) addresses
  • URLs using any of the four previous items as host

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

This API applies to Varnish Enterprise 6.0 and higher. Note ip addresses from SRV records use the port from the SRV record by default. The port from the port argument can be forced by setting port_rule to force.

Goto 4.1 API

VCL example

goto behaves like vmod-directors: you first create a director object in vcl_init 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;

# varnish needs at least one static backend, even if it's null
backend default none;

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

sub vcl_recv {
	set req.backend_hint = 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;
backend default none;
# goto.dns_backend() can only be used in vcl_backend_* routines
sub vcl_backend_fetch {
        set bereq.backend = goto.dns_backend(;

goto has quite a few optional parameters, and these can be specified in any order much like in Python, where each parameter is named:

goto.dns_backend(, ip_version = ipv6)



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 backend-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_director() 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).
  • ENUM port_rule [abide, force] (abide): conform the port to different rules. If force, the port will be determined by s or port. If abide, the port will be determined by SRV record if one has been resolved, otherwise the port is determined by s or port.
  • 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.
  • STRING certificate (none): specifies a client certificate ID for use. See Backend SSL/TLS for more details.
  • 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.
  • ENUM ttl_rule [abide, force, morethan, lessthan] (force): conform the TTL to different rules. If abide, the TTL will be taken from the DNS resolution. If force, the TTL will be taken from the TTL parameter. If morethan, the TTL will be the resolved TTL if it is more than the TTL parameter. Otherwise it is set to the TTL parameter. If lessthan, the TTL will be the resolved TTL if it is less than the TTL parameter. Otherwise it is set to the TTL parameter. A local DNS resolution with a ttl_rule = abide, will set the backend’s ttl to 1s. This can be overwritten by using ttl_rule = force, which will set the ttl to the parameter passed into the function.
  • ENUM ignore_update [never, onerror, onempty] (never): choose when to skip revalidating a backend after a DNS request. never skip a backend revalidation. onerror skips a backend revalidation when there is an error in the DNS request. onempty skips a backend revalidation when there is an error in the DNS request or if the request was successful but returned an empty response For example, no valid record types were available.
  • STRING extra_string (none): Adds a suffix to the generated backend names.

Configuration Files

vmod_goto uses /etc/nsswitch.conf to determine the order of DNS resolution and /etc/hosts for local DNS resolution. To configure the number of retries and timeout time (seconds) edit /etc/resolvconf/resolv.conf.d/tail with:

  options attempts:n
  options timeout:n

If neither of the above are present a default of 2 retries and a timeout time of 5 will be set. The number of retries can be between 0 and 5, while timeout time can be between 0 and 30. For the edits to go into effect, enter sudo resolvconf -u. See man resolv.conf for more information about this configuration.

Counters and backend names

goto will internally create regular Varnish backends, meaning you will see their counters in varnishstat, as for any other backend. However, contrary to regular backends, the names must be generated, and you’ll see backends named following this schema:

# e.g.

The $SERIAL element ensures uniqueness in the case a new backend is created with the same definition as an old one, with both coexisting in varnishstat for a short time.


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.