Run multiple sites on a single Varnish Cache instance — each with its own VCL, its own TLS certificates, and zero downtime when configuration changes.
Give every domain its own config file:
# routes.yaml
routes:
- name: foo_service
hostnames:
- foo.com
- www.foo.com
vclPath: /etc/varnish/foo.vcl
tls:
- cert: /etc/ssl/foo.crt
key: /etc/ssl/foo.key
- name: bar_service
hostnames:
- bar.com
vclPath: /etc/varnish/bar.vclThen generate and apply:
route-builder -reload routes.yaml
# wrote /etc/varnish/routing.vcl
# wrote /etc/varnish/cmdfile
# reloaded 2 route(s)route-builder [options] routes.yaml
route-builder [options] file.vcl [file.vcl ...]
Pass a single .yaml/.yml file, or one or more .vcl files with embedded routing config (see VCL frontmatter).
| Flag | Default | Description |
|---|---|---|
-cmdfile PATH |
/etc/varnish/cmdfile |
Output path for the Varnish cmdfile. Use - for stdout, none to suppress. |
-vclfile PATH |
/etc/varnish/routing.vcl |
Output path for the routing VCL. Use - for stdout, none to suppress. |
-yamlfile PATH |
(unset) | Write parsed config as YAML. Use - for stdout. Incompatible with YAML input. |
-reload |
false | After writing files, reload the running Varnish instance over the admin protocol. |
-timeout DURATION |
30s |
Wall-clock deadline for the entire -reload operation. No effect without -reload. |
-n NAME |
(default instance) | Varnish instance name (the -n argument passed to varnishd). |
-test-route URL |
(unset) | Print which route handles this URL's hostname. Skips file output. |
-version |
Print version and exit. |
Note: -vclfile - requires -cmdfile none — the cmdfile references the routing VCL by path, so both can't be written to stdout simultaneously.
go install github.com/varnish/route-builder@latestOr build from source:
git clone https://github.com/varnish/route-builder
cd route-builder
go build -o route-builder .A systemd service unit is provided in contrib/systemd/ for production deployments.
Each route maps one or more hostnames to a VCL file. route-builder validates the config (no duplicate names, no overlapping hostnames), then generates two files for Varnish:
- routing VCL — dispatches incoming requests to the right VCL label by hostname
- cmdfile — Varnish startup script that loads all VCL files and activates the router
routes.yaml (or foo.vcl bar.vcl ...)
│
▼
[]VCLConfig ──────────────────┐
│ │
▼ ▼
routing.vcl cmdfile
│ │
└──────────┬──────────────┘
▼
varnishd -f cmdfile (or -reload to hot-swap)
If you'd rather keep routing config alongside the VCL itself, embed it as a comment block at the very top of each VCL file:
/* routing
name: foo_service
hostnames:
- foo.com
- www.foo.com
tls:
- cert: /etc/ssl/foo.crt
key: /etc/ssl/foo.key
*/
vcl 4.1;
backend default { .host = "10.0.0.1"; .port = "80"; }Then pass the VCL files directly:
route-builder /etc/varnish/services/*.vclUse -yamlfile to export the parsed frontmatter to a YAML manifest if you want to migrate to the YAML workflow later.
Each route can carry one or more TLS certificates — either a PEM bundle or a separate cert + key pair:
tls:
- pem: /etc/ssl/bundle.pem # cert + key in one file
- cert: /etc/ssl/foo.crt # or separate files
key: /etc/ssl/foo.keyRelative paths are resolved from the YAML file's directory (or the VCL file's directory for frontmatter).
Wildcards are supported in individual domain segments:
hostnames:
- foo.com
- www.foo.com
- "*.api.foo.com"Ports are stripped automatically. TLS requests use the SNI hostname.
-reload applies new configuration to a running Varnish instance without dropping traffic. route-builder performs an 8-stage atomic operation over the admin protocol:
- Snapshot existing route-builder VCL names and TLS cert IDs
- Load each per-route VCL and create its timestamped label
- Compile and load the routing VCL inline
- Load new TLS certificates
- Commit new TLS certificates
- Activate new routing VCL (
vcl.use) — point of no return - Discard old VCL objects
- Discard old TLS certificates
If any step before stage 6 fails, staged changes are rolled back and the running instance is left untouched. The cmdfile and routing VCL are written to disk first, so Varnish can replay them on a cold restart.
# Generate files and reload in one command
route-builder -reload routes.yaml
# Reload only, skip writing files
route-builder -reload -cmdfile none -vclfile none routes.yaml
# Debug: see what routing VCL would be generated
route-builder -vclfile - -cmdfile none routes.yaml
# Debug: check which route handles a hostname
route-builder -test-route https://www.foo.com routes.yaml