azurelamp

Configure multiple applications on the same server with Varnish

Varnish allows configuring multiple applications in the same server. To do this, follow the steps below:

  • Get the VCL files for each application (in this example, Ghost and WordPress):

    $ sudo cp ghost.vcl /opt/bitnami/varnish/etc/varnish/ghost.vcl
    $ sudo sed -i 's/port = "[^"]*"/port = "80"/g' /opt/bitnami/varnish/etc/varnish/ghost.vcl
    
    $ sudo cp wordpress.vcl /opt/bitnami/varnish/etc/varnish/
    $ sudo sed -i 's/port\s*=\s*"[^"]*"/port = "80"/g' /opt/bitnami/varnish/etc/varnish/wordpress.vcl
    
  • Append some code to the default.vcl file that will load the proper VCL file configuration depending on the application selected. This will depend on how you have your applications stored on the server.

    • If your applications are configured based on a URL prefix such as http://DOMAIN/foo corresponding to the foo application and http://DOMAIN/bar pointing to the bar application, use:

      if ( req.url ~ "^/foo/")
      {
      # /foo configuration
      }
      elsif ( req.url ~ "^/bar/")
      {
      # /bar configuration
      }
      
    • If your applications are configured with different domains such as http://wordpress.example.com pointing to your WordPress blog and http://ghost.example.com pointing to Ghost, use:

      if (req.http.Host == "wordpress.example.com")
      {
      # Wordpress configuration
      }
      elsif (req.http.Host == "ghost.example.com")
      {
      # Ghost configuration
      }
      
    • If your applications are accessible on different ports, use

      if (req.http.Host ~ "^wordpress.example.com:[0-9]+")
          # Wordpress configuration
      } elsif (req.http.Host ~ "^ghost.example.com:[0-9]+")  {?
      # Ghost configuration
      }
      

In this example, assume the second approach. Create the new configuration by merging the contents of the different sections (vcl_recv, vcl_fetch, vcl_deliver, *vcl_**) inside wordpress.vcl and ghost.vcl:

        sub vcl_recv {
        if (req.http.Host ~ "^wordpress.example.com:[0-9]+")
            # Wordpress vcl_recv configuration
            ...
            } elsif (req.http.Host ~ "^ghost.example.com:[0-9]+")  {?
            # Ghost vcl_recv configuration
            ...
            }}
        sub vcl_fetch {
        if (req.http.Host ~ "^wordpress.example.com:[0-9]+")
            # Wordpress vcl_fetch configuration

            ...
            } elsif (req.http.Host ~ "^ghost.example.com:[0-9]+")  {?
            # Ghost vcl_fetch configuration

            ...
            }
        }
        sub vcl_deliver {
        if (req.http.Host ~ "^wordpress.example.com:[0-9]+")
            # Wordpress vcl_deliver configuration
            ...
            } elsif (req.http.Host ~ "^ghost.example.com:[0-9]+")  {?
            # Ghost vcl_deliver configuration
            ...
            }
        }

If any of the sections are empty in any of the VCL files, omit them. In addition, if you find additional *vcl_ subst* sections in addition to *vcl_recv*, *vcl_fetch* and *vcl_deliver*, merge them in a similar way. In this scenario, you end up with:

        # Previous contents of default.vcl
        # ....
        # ....

        # Multi-apps configuration:
        # This is a merge of all the different sections of wordpres.vcl and ghost.vcl

        sub vcl_recv {
            if (req.http.Host ~ "^wordpress.example.com:[0-9]+") {
                # Contents of the section vcl_recv in wordpress.vcl
                if (req.http.Accept-Encoding) {
                #revisit this list
                if (req.url ~ "\.(gif|jpg|jpeg|swf|flv|mp3|mp4|pdf|ico|png|gz|tgz|bz2)(\?.*|)$") {
                    remove req.http.Accept-Encoding;
                } elsif (req.http.Accept-Encoding ~ "gzip") {
                    set req.http.Accept-Encoding = "gzip";
                } elsif (req.http.Accept-Encoding ~ "deflate") {
                    set req.http.Accept-Encoding = "deflate";
                } else {
                    remove req.http.Accept-Encoding;
                }
                }
                if (req.url ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|mp4|pdf|ico|png)(\?.*|)$") {
                unset req.http.cookie;
                set req.url = regsub(req.url, "\?.*$", "");
                }
                if (req.http.cookie) {
                if (req.http.cookie ~ "(wordpress_|wp-settings-)") {
                    return(pass);
                } else {
                    unset req.http.cookie;
                }
                }
            } elsif (req.http.Host ~ "^ghost.example.com:[0-9]+") {
                # Contents of the section vcl_recv in ghost.vcl


                # If the client uses shift-F5, get (and cache) a fresh copy. Nice for
                # systems without content invalidation. Big sites will want to disable
                # this.
                if (req.http.cache-control ~ "no-cache") {
                set req.hash_always_miss = true;
                }
                set req.http.x-pass = "false";
                # TODO: I haven't seen any urls for logging access. When the
                # analytics parts of ghost are done, this needs to be added in the
                # exception list below.
                if (req.url ~ "^/(api|signout)") {
                set req.http.x-pass = "true";
                } elseif (req.url ~ "^/ghost" && (req.url !~ "^/ghost/(img|css|fonts)")) {
                set req.http.x-pass = "true";
                }

                if (req.http.x-pass == "true") {
                return(pass);
                }
                unset req.http.cookie;
            }
        }
        sub vcl_fetch {
            if (req.http.Host ~ "^wordpress.example.com:[0-9]+") {
            # Contents of the section vcl_fetch in wordpress.vcl
                if (req.url ~ "wp-(login|admin)" || req.url ~ "preview=true" || req.url ~ "xmlrpc.php") {
                return (hit_for_pass);
                }
                if ( (!(req.url ~ "(wp-(login|admin)|login)")) || (req.request == "GET") ) {
                unset beresp.http.set-cookie;
                }
                if (req.url ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|mp4|pdf|ico|png)(\?.*|)$") {
                set beresp.ttl = 365d;
                }
            } elsif (req.http.Host ~ "^ghost.example.com:[0-9]+") {
            # Contents of the section vcl_fetch in ghost.vcl

            # Only modify cookies/ttl outside of the management interface.
                if (req.http.x-pass != "true") {
                unset beresp.http.set-cookie;
                if (beresp.status < 500 && beresp.ttl == 0s) {
                    set beresp.ttl = 2m;
                }
                }
            }
        }
        sub vcl_deliver {
            if (req.http.Host ~ "^wordpress.example.com:[0-9]+") {
                # Contents of the section vcl_deliver in wordpress.vcl

                # multi-server webfarm? set a variable here so you can check
                # the headers to see which frontend served the request
                #   set resp.http.X-Server = "server-01";
                if (obj.hits > 0) {
                    set resp.http.X-Cache = "HIT";
                } else {
                    set resp.http.X-Cache = "MISS";
                }
            } elsif (req.http.Host ~ "^ghost.example.com:[0-9]+") {
                # Contents of the section vcl_deliver in ghost.vcl

            }
        }
  • Restart Varnish:

    $ /opt/bitnami/ctlscript.sh restart varnish
    

You should now have both configurations working simultaneously depending on the hostname.

You can download the different files used in the example as a reference: