Virtual Registry 0.18.0

Released: 2026-09-10

Varnish version: 6.0.18r4

Artifact Firewall version: 0.6.0

Added

  • Added cluster.storage_replicas, which shards persistent storage across the cluster instead of replicating it. A clustered node caches every object it serves in memory and persists it to disk as well, so a cluster of N nodes holds N copies on disk and its usable persistent cache is that of a single node. Setting storage_replicas: 1 keeps each object on disk only on the node that owns it, pooling the whole cluster’s disk into one cache; a higher count keeps that many copies, trading capacity back for redundancy. Unset it stays full replication. Requires the persistence addon and configured varnish.storage.stores.
  • Added RPM (yum/dnf/zypper) support: .rpm packages and the checksum-named repodata files are cached for the immutable lifetime, repomd.xml revalidates, and where the firewall is enabled repomd.xml and primary.xml are filtered while a .rpm download is authorized per version. Filtering repomd.xml invalidates its signature, so the client needs repo_gpgcheck=0 (per-package gpgcheck=1 keeps working), or firewall.preserve_manifest: true to keep the upstream signature and give up metadata filtering.
  • Go modules now go through the firewall where it is enabled: @v/list is filtered to the versions the ruleset allows, @latest, .info and .mod are gated on the version they resolve to, and a module zip is authorized per version. The checksum database is not a module resource and is untouched.
  • Conda channels now go through the firewall where it is enabled: repodata.json is filtered to the packages the ruleset allows, and a .conda or .tar.bz2 download is authorized per version. The two variants the firewall cannot filter, repodata.json.bz2 and CEP-16 sharded repodata, are refused so the client falls back to one it can.
  • The startup line and --version now carry the revision the binary was built from, as stamped by the Go toolchain, suffixed -dirty for a build off a modified tree. It is omitted when the build carries no stamp.
  • Added ArgoCD UI proxying: the UI and its Dex/OIDC login flow get a full cache bypass and a redirect-follow opt-out, the same treatment already given to the JFrog UI, so the login’s session cookie isn’t dropped by a server-side redirect.
  • Added the /package-reroute endpoint (virtual_registry.package_reroute.enabled, off by default): a network-redirect target for a forward proxy that intercepts outbound traffic to public package registries, such as a Zscaler ZIA rule mirroring JFrog’s Package Traffic Controller. It resolves the upstream host embedded in ?url= against every configured registry’s remotes and re-enters that registry’s normal pipeline.
  • Added Ubuntu 26.04 (Resolute Raccoon) packages. The .deb is the same build already published for the other Debian-family distributions; it is now uploaded to, and installed and upgraded under, ubuntu/resolute as well.
  • Added an optional name to a remote (virtual_registry.registries[].remotes[]) and a cluster peer (cluster.peers[]), used in the generated backend/director name instead of a positional index. Both share the same underlying config type, so the fix covers artifactory_dir_N and cluster_dir_N alike. Names are alphanumeric and hyphens only, and must be unique within their list; an unnamed remote or peer keeps the existing positional naming.

Changed

  • remote_auth now applies to every package type, not just OCI (Docker/container) registries. For OCI it still targets the /token auth-token exchange; for everything else it is attached to the content request itself, in vcl_recv, and an injected request is classified as its own cache entry rather than preflighted, since the caller’s own credential is not what reaches the remote. That entry is shared by every injected caller and keyed without the credential, so rotating the secret in password_env leaves the cached objects in place instead of invalidating them. policy: always combined with auth_policy: preflight therefore skips the preflight check, which a startup warning now calls out. if_anonymous is unaffected: it still only fills in when the caller sent no credential of its own.
  • Reworked what the Supervisor logs. Startup records the Supervisor, varnishd and add-on versions, the config files it read, and the configuration itself as every setting that differs from the defaults, so a log says what a Supervisor was running without its files having to be found. A reload names the settings that changed instead of counting them, and INFO is reserved for state changes rather than a line before and another after each one. The license text and its signature are no longer written to the log when they change, and the container image no longer echoes the versions before startup, since the Supervisor reports them itself.
  • Removed the startup warning about virtual_registry.redirects.deny_unknown_hosts not being set. It fired on every configuration that left the option at its default, and writing the default out verbatim was the only way to silence it.
  • The varnish-supervisor package now installs a Varnish module, /usr/lib/varnish-plus/vmods/libvmod_vizier.so, which the generated VCL imports. Hex requests are now parsed inside that module. Every other ecosystem is unchanged, and caching behaviour behaves the same regardless of it is parsed by the VMOD. Remaining ecosystems will move across one at a time. A Varnish module is ABI-bound to the exact Varnish Enterprise build it was compiled against, so varnish-plus and varnish-supervisor must now be upgraded together. Upgrading varnish-plus alone leaves Varnish unable to load its VCL.

Fixed

  • Fixed a firewall rule scoped to a set of virtual registries never matching a package download. The check that authorizes a download did not name the registry it was made for, and a scoped rule matches nothing without one, so the rule was skipped and the download allowed. Rules that are not registry-scoped, and the filtering of index and metadata documents, were unaffected.
  • Fixed the version of an npm tarball being read from the first hyphen in its filename, so a package whose name contains one was authorized and audit-logged under a mangled version: map-stream-0.0.7.tgz came out as version stream-0.0.7. A rule scoped to a version range could deny a version it allows, or allow one it denies.
  • Fixed supervisor.log_level and supervisor.log_output only taking effect for the config manager itself on reload. Every other component kept the settings it started with, and a change to log_output silenced them for the rest of the run, since the file they were still writing to had been closed. A reload and a SIGHUP now also wait for the records already on their way into the old file before closing it.
  • Fixed the reload line reporting unrelated registries as changed when one was removed from the middle of the list. Registries and stores are now matched by name rather than by position, and the list itself is reported when its members or their order change, the order being what the generated routing rules are built from.
  • Fixed a varnishd message that runs over several lines losing everything after the first line when it followed one of the startup milestones.
  • Fixed a cached certificate entry that could not be deleted being reported as cleared. The failure is now logged, since an entry left behind is read back by the next fetch.
  • Fixed a change to acme.domains that swaps one domain for another, rather than adding or removing one, not reaching ACME. The reload applied, but no certificate was requested for the new domain until the Supervisor was restarted.
  • Fixed the OpenTelemetry pipeline being started before Varnish, so it spent the first seconds of every startup warning that the shared log it reads was not there yet.
  • Fixed a redirect from a remote being followed with the original request’s path instead of the path the redirect pointed at, on deployments with clustering enabled. Any remote that signs its redirect targets rejected the resulting fetch, which made every ghcr.io layer pull fail with 400 Missing query parameters.
  • Fixed PyPI wheel and sdist downloads not being authorized against the firewall, so a client holding the URL already, from a lock file or a cached index, was served a version a rule denies. The download is now checked per version, as it is for every other ecosystem.
  • Fixed Maven .pom files at a released version revalidating against the remote on every request, which cost a round trip per descriptor in a resolve. They are now cached for the immutable lifetime, while .pom files under a -SNAPSHOT version keep revalidating.
  • Fixed the systemd scriptlets in the varnish-supervisor RPM, which never ran. An install only logged a warning, but an upgrade failed the transaction and left both versions recorded as installed. Upgrading from 0.17.0 or earlier still trips on the old package’s own scriptlets: run the upgrade again, then drop the stale record with rpm -e --noscripts varnish-supervisor-<old version> and restart the service.
  • Fixed a DEB upgrade re-enabling and starting the Supervisor where it had deliberately been disabled or stopped. An upgrade now restarts it only if it was already running. Upgrading from 0.17.0 or earlier is the exception, and still enables and starts it: those versions stop and disable the unit from their own prerm, which runs before anything in the new package does, so there is no state left to preserve.
  • Fixed systemctl stop varnish-supervisor leaving the service in a failed state. Varnish was killed outright instead of being asked to stop, which also meant the shutdown_delay and shutdown_close parameters never took effect.
  • Fixed the Supervisor hanging until systemd killed it when it was stopped while a varnishadm command was still in flight, which is most likely when the service is stopped shortly after it starts.

®Varnish Software, Wallingatan 12, 111 60 Stockholm, Organization nr. 556805-6203