Deployment guide

Vinyl Cache 9.1.0

Overview

This guide covers launching and verifying Vinyl Cache 9.1.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch. The listing's guide hub describes the other VPN, proxy and remote access servers in the listing.

Vinyl Cache is an HTTP reverse-proxy cache, also called a web application accelerator. It sits in front of your web server (the origin or backend), answers repeat requests from memory, and asks the origin only for what it does not have or what has expired. You decide what is cached, for how long, and how requests and responses are rewritten in VCL, its configuration language.

Vinyl Cache is the open-source project formerly released as Varnish Cache. It was renamed in 2026, and the 9.x series continues the same code base. It is a headless network service with no web console.

What you get:

  • Port 80 is the cache. It ships pointed at a small demo origin (nginx on 127.0.0.1:8080, reachable only from the instance itself), so the second request for a page is answered from the cache with X-Cache: HIT.
  • sudo vinylctl backend <host:port> points the cache at your own origin (Step 6).
  • The management interface (vinyladm) listens on 127.0.0.1:6082 only and needs a secret generated on this instance.
  • The current upstream release, built from source. Ubuntu 24.04 carries no Vinyl Cache package. Its varnish 7.1 package is in the community-maintained universe component and predates several security fixes. This image ships Vinyl Cache 9.1.0, built from the unmodified vinyl-cache.org release after its SHA-256 checksum was checked against the one the project publishes. 9.1.0 includes the fix for the VSV00020 advisory. The build tools are removed before the image is captured. The C compiler stays because Vinyl Cache compiles its VCL configuration to C every time it loads it.
  • A unique management secret per instance, generated on first boot. No secret is baked into the image. On first boot, the instance generates its own 256-bit secret for the management interface. The cache cannot start until it exists.
  • Management on loopback only. The management interface listens on 127.0.0.1:6082, answers nothing but a challenge until the client proves the secret, and is never reachable from the network. vinylctl uses it for you as root.
  • PURGE only from the instance itself. PURGE requests are honored only from 127.0.0.1. From anywhere else they get 405. BAN over HTTP is refused. Bans go through the management interface.
  • First boot proves the cache. Before it marks the instance ready, first boot checks that a page is a cache MISS and then a HIT through port 80. It also checks that the management interface accepts this instance's secret and refuses a wrong one. If any check fails, the cache stays stopped.
  • Hardened services. The cache runs in Vinyl Cache's jail as two unprivileged system users: vinyl for the manager, and vcache for the worker that handles traffic, with no capabilities. It runs inside a systemd sandbox with a read-only system, no access to home directories or devices, a restricted set of system calls and core dumps disabled. The demo origin runs entirely as www-data with no privileges. A guard refuses to start the cache with a secret that is a link, readable by other users or too short, or with a VCL file others can write.
  • No route to the instance metadata service. The cache and the demo origin are blocked from the EC2 instance metadata service at the system level (IPv4 and IPv6). No backend anyone configures can be turned into a way to read the instance's credentials.
  • Honest client addresses and quiet headers. The shipped VCL sends your origin the client's real address from the TCP connection in X-Forwarded-For (a value the client sent is replaced). It also removes the Via header that would reveal the software version and the instance's host name.
  • Key-only SSH. Password and keyboard-interactive login and root login are disabled for SSH.

Vinyl Cache does not terminate TLS itself. For HTTPS, put an Application Load Balancer, Amazon CloudFront or another TLS terminator in front of port 80 (Step 10).

Prerequisites

  • An AWS account and permission to launch EC2 instances.
  • An EC2 key pair in the Region you launch in.
  • A web server to cache (your origin) that is reachable from the instance, or just the bundled demo origin to try it out.
  • Recommended instance type: m5.large. The cache keeps objects in memory. The image reserves up to 1 GB for cached objects, which you can raise for larger instances (see "Cache size").

Connecting to your instance

The operating system is Ubuntu 24.04 LTS (Vinyl Cache 9.1.0 on Ubuntu 24.04), and the SSH user is ubuntu. These ports are in use:

  • Port 22 (TCP): SSH administration. Your administrators should reach it.
  • Port 80 (TCP): the cache (HTTP). Your clients, your load balancer or your CDN should reach it.
  • Port 6082 (TCP): management interface, on 127.0.0.1 only. Nobody else should reach it. Never open it.
  • Port 8080 (TCP): demo origin, on 127.0.0.1 only. Nobody else should reach it. Never open it.
1

Launch the instance

Option A: AWS Marketplace console

  • Open the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  • Once the subscription is active, choose Launch your software.
  • Choose One-click launch from AWS Marketplace, or Launch from EC2 Console for the full launch wizard. In Version, choose Vinyl Cache 9.1.0 on Ubuntu 24.04, then choose your Region.
  • Pick an instance type and your key pair. m5.large is recommended, and the software charge is $0.04 per vCPU per hour.
  • Set the security group. The listing's default group allows SSH (22) and the cache (80). Restrict 22 to your administrators. Restrict 80 to your load balancer or CDN once you put one in front.
  • Choose your VPC and subnet and leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.

Option B: AWS CLI

Run this on your own workstation. It creates a security group that allows SSH and the cache from your own address range only, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR range with your own:

SG=$(aws ec2 create-security-group --group-name vinyl-cache \
  --description "Vinyl Cache" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text) for p in 22 80; do aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port "$p" --cidr <your-mgmt-cidr> done aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=vinyl-cache}]'

You do not need --metadata-options, because the AMI already makes IMDSv2 required.

2

Connect and read the instance details

Connect as ubuntu with the key pair you chose:

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot finishes within a minute of launch. It generates this instance's management secret, starts the demo origin and the cache, and checks them. It then writes /root/vinyl-cache-info.txt, which only root can read. This command prints the file without the secret's fingerprint line:

sudo grep -v '^MGMT_SECRET_SHA256=' /root/vinyl-cache-info.txt
# Vinyl Cache 9.1.0 - written by vinyl-firstboot (firstboot). Nothing below is a secret; the management CLI secret itself is
# /etc/vinyl/secret (root:root 0600): anyone holding it can reconfigure the cache, so it never leaves this instance.
# VINYL_HTTP_PORT=80 VINYL_VPC_IP=172.31.20.15 VINYL_PUBLIC_IP=203.0.113.10 VINYL_PUBLIC_HOSTNAME=ec2-203-0-113-10.compute-1.amazonaws.com VINYL_CLI=127.0.0.1:6082 (loopback only) MGMT_SECRET_FILE=/etc/vinyl/secret VINYL_BACKEND=127.0.0.1:8080 DEMO_ORIGIN=127.0.0.1:8080 (loopback only)
#
# Point the cache at your origin:
sudo vinylctl backend 10.0.1.25:8080
# Prove caching (X-Cache: HIT):
sudo vinylctl hitcheck Self-test:
sudo vinyl-selftest
# Management CLI:
sudo vinylctl adm status New CLI secret:
sudo vinylctl rotate-secret
# Guide: https://secureanycloud.com/stacks/vinyl-cache-9-1-0/

VINYL_VPC_IP is the instance's private address. VINYL_PUBLIC_IP and VINYL_PUBLIC_HOSTNAME are its public address and name (none in a subnet without public addresses). VINYL_BACKEND is the origin the cache currently uses.

The secret's fingerprint (MGMT_SECRET_SHA256) is not secret, but /etc/vinyl/secret is.

3

Confirm the cache is running

systemctl is-active vinyl-cache vinyl-origin vinyl-cache-firstboot
sudo ss -Hltn | awk '{print $4}' | sort
active active active 0.0.0.0:22 0.0.0.0:80 127.0.0.1:6082 127.0.0.1:8080 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:80

The cache listens on port 80 (IPv4 and IPv6). The management interface (6082) and the demo origin (8080) listen on loopback only. The two :53 sockets are Ubuntu's local DNS resolver, which also listens on loopback only.

4

Run the self-test

The image includes a self-test that checks the cache and the management interface end to end. It never prints the secret:

sudo vinyl-selftest
ok /etc/vinyl/secret matches /root/vinyl-cache-info.txt (fe1ec3ada41f...) ok CLI 127.0.0.1:6082: this instance's secret -> 200 PONG; a wrong secret -> 500 + closed; no secret -> 101 for vcl.list ok :80 -> backend: first request MISS, second HIT (no Via header) SELFTEST_OK

The test requests a fresh URL twice through port 80 (the first must be a MISS, the second a HIT). It logs in to the management interface with this instance's secret. It also checks that a wrong secret is refused and the connection closed, and that a client that has not logged in cannot run commands. First boot runs the same test before it marks the instance ready.

5

See caching at work

On the instance, vinylctl hitcheck requests the front page twice and shows the second response's cache headers:

sudo vinylctl hitcheck
HTTP/1.1 200 OK Age: 0 X-Cache: HIT

From your own machine, request a page twice through the public address and compare the X-Cache header (MISS, then HIT). The Age header counts the seconds the object has been in the cache:

curl -s -D - -o /dev/null http://<public-ip>/ | grep -iE '^(x-cache|age|server):'
curl -s -D - -o /dev/null http://<public-ip>/ | grep -iE '^(x-cache|age|server):'

A full response from the demo origin through the cache looks like this:

curl -s -D - -o /dev/null http://127.0.0.1/ | tr -d '\r'
HTTP/1.1 200 OK Server: nginx Date: Sun, 01 Nov 2026 01:22:45 GMT Content-Type: text/html Content-Length: 575 Last-Modified: Sun, 01 Nov 2026 01:19:19 GMT ETag: "6ae69397-23f" Cache-Control: public, max-age=120 X-Vinyl: 13 11 Age: 0 Accept-Ranges: bytes X-Cache: HIT Connection: keep-alive

Server comes from your origin (the demo origin does not reveal its version). X-Vinyl holds the transaction IDs that Vinyl Cache's logging tools use.

Responses that Vinyl Cache generates itself, such as error pages, carry Server: Vinyl-Cache without a version. No Via header is sent.

6

Point the cache at your own origin

Give vinylctl backend the IPv4 address or DNS name and the port of your web server. It writes the backend into /etc/vinyl/default.vcl and has Vinyl Cache compile the new configuration first. Nothing changes if it does not compile, for example because a DNS name does not resolve. Otherwise it switches to the new configuration without dropping connections or the cache:

sudo vinylctl backend <private-ip>:8080
sudo vinylctl status
Vinyl Cache 9.1.0 (sac) cache vinyl-cache.service : active origin vinyl-origin.service : active (demo, 127.0.0.1:8080) listening: 0.0.0.0:80 127.0.0.1:6082 127.0.0.1:8080 [::]:80 backend: host = "10.0.1.25"; port = "8080"; counters: MAIN.client_req 7 MAIN.cache_hit 3 MAIN.cache_miss 3

Keep your origin reachable only from the cache, for example with a security group rule on the origin that allows only the cache instance's security group.

Vinyl Cache passes the request's Host header to the origin unchanged and caches each host name separately, so one cache can serve several sites from one origin. To accept only your own domain names, add a check to vcl_recv (Step 7).

A DNS name is resolved when the VCL is loaded, so run sudo vinylctl reload after its address changes. To go back to the demo origin, run sudo vinylctl reset-demo.

vinyl-selftest is written for the demo origin. Once the cache points at your own origin, check caching with curl against a page your origin marks cacheable instead. When you no longer need the demo origin, stop it with sudo systemctl disable --now vinyl-origin.

7

Change the caching policy (VCL)

The configuration is /etc/vinyl/default.vcl. As shipped, it has one backend, a PURGE rule and the response headers described above. Everything else is Vinyl Cache's built-in behavior:

  • Requests with a Cookie or Authorization header, and methods other than GET and HEAD, are passed to the origin without caching.
  • The origin's Cache-Control and Expires headers decide how long an object is cached. It is cached for 120 seconds when the origin sends neither.
  • Responses that set cookies are not cached.
  • Requests without a Host header, or with an unknown method, are refused.
sudo grep -vE '^[[:space:]]*(#|$)' /etc/vinyl/default.vcl | head -12
vcl 4.1; backend default { .host = "127.0.0.1"; .port = "8080"; .connect_timeout = 3s; .first_byte_timeout = 60s; .between_bytes_timeout = 60s; } acl sac_purge { "127.0.0.1"; "::1"; }

Edit the file with sudoedit /etc/vinyl/default.vcl, then apply it. The reload compiles the file under a new name and switches to it only if it compiles. If it does not compile, the running configuration keeps serving and the error is printed:

sudo vinylctl reload
sudo vinylctl adm vcl.list
VCL reloaded. discarded cold busy 2 boot active auto warm 0 sac_20261101_013344_118174

The new configuration is active at once. The previous one is discarded and disappears when the last connection still using it closes.

For example, to cache only www.example.com and refuse other host names, add this at the top of sub vcl_recv:

if (req.http.host != "www.example.com") { return (synth(404, "Unknown site")); }

The VCL reference is in man vcl and man vcl-var, and online in the Vinyl Cache documentation. Inline C in VCL is disabled. Keep the file owned by root and not writable by others, because the guard refuses to load it otherwise.

8

Purge cached objects

To remove one URL from the cache, send a PURGE request for it from the instance itself. vinylctl purge does that for the host name 127.0.0.1. For another host name, send the request with curl and that Host header:

sudo vinylctl purge / curl -s -o /dev/null -w '%{http_code}\n' -X PURGE -H 'Host: 127.0.0.1' http://127.0.0.1/
200 200

The same request from any other address, including through the instance's own public or private address, is refused:

curl -s -o /dev/null -w '%{http_code}\n' -X PURGE http://<public-ip>/
405

To invalidate many objects at once (for example everything under /images/), add a ban through the management interface:

sudo vinylctl adm ban req.url '~' '^/images/'
sudo vinylctl adm ban.list
Present bans: 1793496824.965254 0 - req.url ~ ^/images/ 1793496776.556781 1 C

A ban applies to everything already in the cache that matches it. Vinyl Cache checks objects against the ban lazily and drops the ban once nothing older than it is left.

9

The management interface

vinylctl adm runs any Vinyl Cache management command over the loopback interface with this instance's secret. vinyladm reads the secret from /etc/vinyl/secret itself, so the secret never appears on a command line:

sudo vinylctl adm status
sudo vinylctl adm param.show default_ttl | head -2
Child in state running default_ttl Value is: 120.000 [seconds] (default)

sudo vinylctl adm help lists the commands. A runtime parameter change with param.set lasts until the cache restarts. To make one permanent, add -p name=value to the ExecStart line with sudo systemctl edit --full vinyl-cache and restart the cache.

The statistics and log tools (vinylstat, vinyllog, vinylncsa, vinyltop, vinylhist) read the cache's shared memory directly and need sudo.

10

HTTPS and load balancers

Vinyl Cache serves plain HTTP. For HTTPS, terminate TLS in front of it:

  • Application Load Balancer: use an HTTPS listener with an AWS Certificate Manager certificate, forwarding to a target group on port 80 of the instance. Then allow port 80 on the instance only from the load balancer's security group.
  • Amazon CloudFront: use the instance (or a load balancer in front of it) as an HTTP origin.

Behind a load balancer, the shipped VCL sends your origin the load balancer's address in X-Forwarded-For, because it trusts only the TCP connection. To pass on the client address that the load balancer reports instead, trust the header only from the load balancer's subnets.

Replace the set req.http.X-Forwarded-For = client.ip; line in vcl_recv with the following. Add the acl block next to sac_purge, using your load balancer subnets:

acl sac_lb { "10.0.0.0"/24; "10.0.1.0"/24; }
# in sub vcl_recv, instead of: set req.http.X-Forwarded-For = client.ip; if (client.ip !~ sac_lb) { set req.http.X-Forwarded-For = client.ip; }

Then run sudo vinylctl reload. Never trust X-Forwarded-For from addresses outside your load balancer subnets.

11

Rotate the management secret (optional)

sudo vinylctl rotate-secret

The command generates a new secret and checks that the management interface accepts it and refuses the old one. It then updates MGMT_SECRET_SHA256 in /root/vinyl-cache-info.txt and runs the self-test. The cache keeps running and keeps its contents.

The rotation runs as a system job, so a dropped SSH connection cannot leave it half-done. If it fails, the previous secret stays in place, and journalctl -u vinyl-rotate-secret says why.

12

Statistics and logs

sudo vinylstat -1 -f MAIN.client_req -f MAIN.cache_hit -f MAIN.cache_miss
MAIN.client_req 3 0.06 Good client requests received MAIN.cache_hit 1 0.02 Cache hits MAIN.cache_miss 1 0.02 Cache misses
  • sudo vinylncsa prints a live access log in the familiar combined format. Use sudo vinylncsa -d | tail for what is already in memory. sudo vinyllog prints the detailed transaction log.
  • The service log is in the journal: sudo journalctl -u vinyl-cache -n 20 --no-pager. Vinyl Cache records management sessions there, including failed logins.
  • The demo origin logs each request it serves to the journal, with the address it saw in X-Forwarded-For: sudo journalctl -t vinyl_origin -n 5 --no-pager.
  • First boot logs to /var/log/sac/sac-firstboot.log. No log contains the secret.

Cache size

The cache stores objects in memory (-s malloc,1g, up to 1 GB of cached objects). Vinyl Cache's own overhead comes on top.

On a larger instance, raise the size to about half to three quarters of the instance's memory. Run sudo systemctl edit --full vinyl-cache and change -s malloc,1g on the ExecStart line (for example -s malloc,24g on a 32 GB instance). Save, then run sudo systemctl restart vinyl-cache. A restart empties the cache.

This build uses the standard system memory allocator. The upstream Linux default is jemalloc, which is not part of Ubuntu's supported main archive. The trade-off is somewhat higher memory fragmentation under heavy churn, so leave some headroom.

How the secure start works

  • vinyl-cache-firstboot.service runs once, on the instance's first boot. It reads the instance's addresses from the instance metadata service (IMDSv2). It generates a 256-bit secret with the kernel's random number generator into /etc/vinyl/secret (owner root, mode 0600).
  • vinyl-cache.service starts only when the ready marker exists, so it can never run without this instance's own secret. Before each start and reload, vinyl-guard checks the secret (root-owned, not a link, not readable by other users, at least 256 bits). It also checks the VCL (root-owned, not writable by others, no inline C).
  • First boot then starts the demo origin and the cache and runs the self-test: a MISS then a HIT through port 80, this instance's secret accepted, a wrong secret refused. Only then does it write /root/vinyl-cache-info.txt and mark first boot done. If any step fails, the cache is stopped, the secret and the ready marker are removed, and the next boot starts over.

Security recommendations

  • Restrict port 22 to your administrators and, once a load balancer or CDN is in front, port 80 to it. Never open 6082 or 8080.
  • Keep your origin reachable only from the cache, and trust X-Forwarded-For only from your own load balancer subnets (Step 10).
  • Review what you cache. Never cache personalized pages. The built-in rules already pass requests with cookies or Authorization headers to the origin.
  • Keep the instance patched: sudo apt-get update && sudo apt-get upgrade. Unattended security updates are enabled for the operating system. Vinyl Cache itself is a source build, so take new SecureAnyCloud versions of this image for Vinyl Cache updates, and watch the Vinyl Cache security advisories.

Known residuals in this version

  • A C compiler is installed on purpose. Vinyl Cache translates every VCL file to C and compiles it each time the configuration loads. So gcc and libc6-dev (both from Ubuntu's supported main component) are part of how the cache works, not leftovers from the build. No other development package and no build tree ship on the image. Ubuntu lists unfixed advisories against gcc-13. They all concern the C++ library, which Vinyl Cache (a C program) does not use.
  • Unfixed Ubuntu advisories in pcre2. At the time of this build, Ubuntu lists seven advisories against its PCRE2 library with no fix released. None is reachable through this image as shipped. The regular expressions Vinyl Cache runs come from the VCL you write, not from client requests. Bans go only through the root-only management interface. Vinyl Cache does not use the affected DFA, invalid-UTF or 32-bit code paths.
  • Unattended security updates install Ubuntu's fixes for both packages when they are released. SecureAnyCloud re-checks this version whenever an Ubuntu security update for pcre2 or gcc-13 appears.

Support and licensing

Vinyl Cache is open-source software released under the BSD 2-Clause license by the Vinyl Cache project. The license text and the build provenance are in /usr/local/share/doc/vinyl-cache/. The source is available from vinyl-cache.org.

Varnish is a trademark of Varnish Software AS. SecureAnyCloud is not affiliated with or endorsed by the Vinyl Cache project or Varnish Software. For help with this image, contact SecureAnyCloud support through the AWS Marketplace listing.

Deploy on AWS

Launch Vinyl Cache 9.1.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS