Vinyl Cache 9.1.0 on AWS User Guide
Overview
This guide covers launching and verifying Vinyl Cache 9.1.0 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg 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 other VPN, proxy and remote access servers in the listing are described on the listing's guide hub.
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 only asks the origin 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.
This image ships ready to show caching straight away:
- 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 withX-Cache: HIT. sudo vinylctl backend <host:port>points the cache at your own origin (Step 6).- The management interface (
vinyladm) listens on127.0.0.1:6082only and needs a secret generated on this instance.
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).
What cloudimg adds
- The current upstream release, built from source. Ubuntu 24.04 carries no Vinyl Cache package (its
varnish7.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 again 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.vinylctluses it for you as root. - PURGE only from the instance itself.
PURGErequests are honoured only from127.0.0.1; from anywhere else they get405.BANover 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, and 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 (
vinylfor the manager,vcachefor the worker that handles traffic, with no capabilities), 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 aswww-datawith 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), so 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), and removes theViaheader 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.
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), 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
| Version | Operating system | SSH user |
|---|---|---|
| Vinyl Cache 9.1.0 on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
| Port | Protocol | Purpose | Who should reach it |
|---|---|---|---|
| 22 | TCP | SSH administration | Your administrators |
| 80 | TCP | The cache (HTTP) | Your clients, your load balancer or your CDN |
| 6082 | TCP | Management interface, 127.0.0.1 only |
Nobody else (never open it) |
| 8080 | TCP | Demo origin, 127.0.0.1 only |
Nobody else (never open it) |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace and 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 (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - 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, leave the metadata defaults (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, and 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: the AMI already makes IMDSv2 required.
Step 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, checks them and then writes /root/vinyl-cache-info.txt, readable only by root. This command prints it 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://www.cloudimg.co.uk/guides/vinyl-cache-9-1-0-on-ubuntu-24-04-aws/
VINYL_VPC_IP is the instance's private address and VINYL_PUBLIC_IP / VINYL_PUBLIC_HOSTNAME 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.
Step 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.
Step 4: Run the self-test
The image includes a self-test that checks the cache and the management interface end to end without printing 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
It requests a fresh URL twice through port 80 (the first must be a MISS, the second a HIT), logs in to the management interface with this instance's secret, and 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.
Step 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). Age 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 Vinyl Cache's logging tools use. Responses Vinyl Cache generates itself, such as error pages, carry Server: Vinyl-Cache without a version. No Via header is sent.
Step 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, has Vinyl Cache compile the new configuration first (nothing changes if it does not compile, for example because a DNS name does not resolve) and then switches to it without dropping connections or the cache:
sudo vinylctl backend <private-ip>:8080
sudo vinylctl status
Vinyl Cache 9.1.0 (cloudimg)
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 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.
Step 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 behaviour: 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 (120 seconds when it sends neither), responses that set cookies are not cached, and 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 cloudimg_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 only switches to it if it compiles; the running configuration keeps serving if it does not, and the error is printed:
sudo vinylctl reload
sudo vinylctl adm vcl.list
VCL reloaded.
discarded cold busy 2 boot
active auto warm 0 cloudimg_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: the guard refuses to load it otherwise.
Step 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 it lazily and drops the ban once nothing older than it is left.
Step 9: The management interface
vinylctl adm runs any Vinyl Cache management command over the loopback interface with this instance's secret, which vinyladm reads from /etc/vinyl/secret itself (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 it. The statistics and log tools (vinylstat, vinyllog, vinylncsa, vinyltop, vinylhist) read the cache's shared memory directly and need sudo.
Step 10: HTTPS and load balancers
Vinyl Cache serves plain HTTP. For HTTPS, terminate TLS in front of it:
- Application Load Balancer: 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 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 and add the acl block next to cloudimg_purge, using your load balancer subnets:
acl cloudimg_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 !~ cloudimg_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.
Step 11: Rotate the management secret (optional)
sudo vinylctl rotate-secret
It generates a new secret, checks that the management interface accepts it and refuses the old one, 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.
Step 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 vinylncsaprints a live access log in the familiar combined format (sudo vinylncsa -d | tailfor what is already in memory), andsudo vinyllogthe 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/cloudimg-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 it to about half to three quarters of the instance's memory: run sudo systemctl edit --full vinyl-cache, change -s malloc,1g on the ExecStart line (for example -s malloc,24g on a 32 GB instance), save, and 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.serviceruns once, on the instance's first boot. It reads the instance's addresses from the instance metadata service (IMDSv2) and generates a 256-bit secret with the kernel's random number generator into/etc/vinyl/secret(owner root, mode0600).vinyl-cache.servicestarts only when the ready marker exists, so it can never run without this instance's own secret. Before each start and reload,vinyl-guardchecks the secret (root-owned, not a link, not readable by other users, at least 256 bits) and 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.txtand 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-Foronly from your own load balancer subnets (Step 10). - Review what you cache: never cache personalised pages (the built-in rules already pass requests with cookies or
Authorizationheaders 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 cloudimg versions of this image for Vinyl Cache updates and watch the Vinyl Cache security advisories.
Support and licensing
Vinyl Cache is open-source software released under the BSD 2-Clause licence by the Vinyl Cache project. The licence 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; cloudimg is not affiliated with or endorsed by the Vinyl Cache project or Varnish Software. For help with this image, contact cloudimg support through the AWS Marketplace listing.