Separate VCL files

Having multiple different vhosts in the same Varnish is a very typical use-case, and from Varnish 5.0 it is possible to have a separate VCL files for separate vhosts or any other distinct subset of requests.

Assume that we want to handle varnish.org with one VCL file and varnish-cache.org with another VCL file.

First load the two VCL files:

vcl.load vo_1 /somewhere/vo.vcl
vcl.load vc_1 /somewhere/vc.vcl

These are 100% normal VCL files, as they would look if you ran only that single domain on your varnish instance.

Next we need to point VCL labels to them:

vcl.label l_vo vo_1
vcl.label l_vc vc_1

Next we write the top-level VCL program, which branches out to the other two, depending on the Host: header in the request:

import std;

# We have to have a backend, even if we do not use it
backend default { .host = "127.0.0.1"; }

sub vcl_recv {
    # Normalize host header
    set req.http.host = std.tolower(req.http.host);

    if (req.http.host ~ "\.?varnish\.org$") {
        return (vcl(l_vo));
    }
    if (req.http.host ~ "\.?varnish-cache\.org$") {
        return (vcl(l_vc));
    }
    return (synth(302, "http://varnish-cache.org"));
}

sub vcl_synth {
    if (resp.status == 301 || resp.status == 302) {
        set resp.http.location = resp.reason;
        set resp.reason = "Moved";
        return (deliver);
    }
}

Finally, we load the top level VCL and make it the active VCL:

vcl.load top_1 /somewhere/top.vcl
vcl.use top_1

If you want to update one of the separated VCLs, you load the new one and change the label to point to it:

vcl.load vo_2 /somewhere/vo.vcl
vcl.label l_vo vo_2

If you want to change the top level VCL, do as you always did:

vcl.load top_2 /somewhere/top.vcl
vcl.use top_2

Details, details, details:

  • All requests always start in the active VCL - the one from vcl.use

  • Only VCL labels can be used in return(vcl(name)). Without this restriction the top level VCL would have to be reloaded every time one of the separate VCLs were changed.

  • You can only switch VCLs from the active VCL. If you try it from one of the separate VCLs, you will get a 503

  • You cannot remove VCL labels (with vcl.discard) if any VCL contains return(vcl(name_of_that_label))

  • You cannot remove VCLs which have a label attached to them.

  • This code is tested in testcase c00077

  • This is a very new feature, it may change

  • We would very much like feedback how this works for you