Networking Azure

LanCache on Ubuntu 24.04 on Azure User Guide

| Product: LanCache on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and use of LanCache on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. LanCache is a self-hosted caching server for large game and content downloads. It transparently caches HTTP downloads from an allow-listed set of game-content CDNs — Steam, Epic Games, Riot, Blizzard/Battle.net, Origin/EA, GOG, Windows Update and more — so a file downloaded once is served from your local network at LAN speed to every other machine, instead of being pulled over the internet again and again.

LanCache has two parts, and this image ships both as the official, pinned upstream containers running under Docker:

  • The cachelancachenet/monolithic, an nginx transparent cache on ports 80 (HTTP) and 443 (HTTPS SNI passthrough).
  • The DNSlancachenet/lancache-dns, which answers the cached CDN domains with the cache's own IP so clients are steered to the cache automatically. This image runs it in a hardened, closed configuration (see below).

There is no login and no credential of any kind — a cache and DNS service has no user interface. Because the appliance is reachable on the network by design, cloudimg closes the two ways an internet-facing cache could be abused, and proves both on every build before the image is captured:

  • It is not an open DNS resolver. The DNS answers only the allow-listed CDN domains (authoritatively, with this VM's IP) and returns REFUSED for every other name — no recursion and no forwarding, so it cannot be used for DNS amplification.
  • It is not an open proxy or TLS relay. The cache proxies only allow-listed CDN hosts on port 80, and relays only allow-listed SNIs on port 443. An arbitrary HTTP Host is refused and an arbitrary TLS SNI is dropped.

At first boot, lancache-firstboot.service resolves this VM's own public IP, writes the per-VM DNS zone so the cached domains resolve to it, and starts both containers. Cache data lives on the OS disk under /var/lib/lancache.

What is included:

  • The official lancachenet/monolithic cache and lancachenet/lancache-dns DNS, pulled and pinned by SHA-256 digest (never :latest) and verified against the pin at build time

  • lancache-firstboot.service, a systemd oneshot that resolves the per-VM public IP, renders the DNS zone and starts both containers on the first customer boot

  • A closed DNS: authoritative-only zones for the cached CDN domains, recursion no, so arbitrary names are REFUSED

  • A hardened cache: an nginx allow-list guard on port 80 and an SNI allow-list on port 443, so only the cached CDNs are served

  • The allow-list of game-content CDN domains, extracted from the official lancache-dns image (the uklans/cache-domains data)

  • Cache storage on a 64 GiB OS disk under /var/lib/lancache, expandable for larger caches

  • Ubuntu 24.04 LTS base with latest security patches applied at build time and unattended security updates enabled

  • Azure Linux Agent for seamless cloud integration and SSH key injection

  • 24/7 cloudimg support with a guaranteed 24 hour response SLA

Prerequisites

  • An active Azure subscription

  • A subscription to the LanCache on Ubuntu 24.04 listing on Azure Marketplace

  • An SSH public key for VM authentication

  • A virtual network and subnet in the target region, reachable by the client machines you want to serve

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM). LanCache is very light on CPU and memory — the two containers together use well under 100 MB of RAM at idle. The resource that matters for a cache is disk: the more storage you give it, the more content it can hold. For a large LAN party or a busy venue, move the cache data to a larger disk (see the last section).

Network security group (NSG) ports. Open these to the client networks that will use the cache:

  • UDP and TCP 53 — DNS (clients point their resolver here)
  • TCP 80 — HTTP cache
  • TCP 443 — HTTPS (SNI passthrough) cache
  • TCP 22 — SSH, from your management IP range only

Scope 53/80/443 to your LAN or client source ranges. The appliance is hardened against open-resolver and open-proxy abuse, but there is no reason to expose a download cache to the whole internet.

Step 1: Deploy from the Azure Portal

Navigate to Marketplace in the Azure Portal, search for LanCache, select the cloudimg publisher entry, and click Create.

On the Networking tab attach a network security group that allows inbound TCP 22 from your management IP range, and inbound UDP+TCP 53, TCP 80 and TCP 443 from your client source ranges. Click Review + create, wait for validation, then Create. Deployment takes around two minutes.

Step 2: Deploy from the Azure CLI

RG="lancache-prod"
LOCATION="eastus"
VM_NAME="lancache-01"
ADMIN_USER="azureuser"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/lancache-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"

az group create --name "$RG" --location "$LOCATION"

az vm create \
  --resource-group "$RG" \
  --name "$VM_NAME" \
  --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s \
  --admin-username "$ADMIN_USER" \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard

# Open the LanCache ports (scope the source ranges to your client networks)
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 53  --priority 1010
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 80  --priority 1020
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 443 --priority 1030

SSH in as your admin user once the VM is running:

ssh azureuser@<vm-ip>

Step 3: Confirm both containers are running

First boot resolves this VM's public IP and starts the cache and the DNS. Confirm the two containers are up, the DNS/HTTP/HTTPS ports are listening, and the cache heartbeat is healthy.

sudo docker ps --format '{{.Names}}  {{.Status}}'
systemctl is-active docker lancache-firstboot
sudo ss -lntup | grep -E ':53 |:80 |:443 '
curl -s -o /dev/null -w 'heartbeat -> HTTP %{http_code}\n' http://127.0.0.1/lancache-heartbeat

You should see lancache-monolithic and lancache-dns both Up, docker and lancache-firstboot both active, sockets listening on 53, 80 and 443, and the heartbeat returning HTTP 204.

lancache-monolithic and lancache-dns both up, ports 53/80/443 listening, cache heartbeat returning HTTP 204

Step 4: Confirm the DNS is closed to abuse

The DNS answers the cached CDN domains with this VM's IP, and refuses everything else. The commands below use the first domain from the shipped allow-list; the appliance serves well over a hundred CDN domains.

CACHE_IP=$(sudo grep '^LANCACHE_IP=' /root/lancache-credentials.txt | cut -d= -f2-)
CDN=$(head -1 /var/lib/lancache/dns/cache-domains.txt)
echo "cache IP = $CACHE_IP   sample cached domain = $CDN"

# A cached CDN domain (and any subdomain of it) resolves to this cache
dig +short @127.0.0.1 "$CDN" A
dig +short @127.0.0.1 "sub.$CDN" A

# An arbitrary domain is REFUSED - this is NOT an open resolver
dig @127.0.0.1 example.com A | grep -E 'status:'

The two dig +short lookups return the cache IP; the example.com lookup returns status: REFUSED with no answer. That is the appliance refusing to recurse for anything outside its allow-list.

A cached CDN domain and its subdomains resolving to the cache IP while example.com is REFUSED with zero answers, over 121 CDN domains served

Step 5: Confirm the cache is closed to abuse

The cache serves the allow-listed CDNs only. An arbitrary HTTP Host is refused (not proxied), and an arbitrary TLS SNI is dropped (not relayed).

CDN=$(head -1 /var/lib/lancache/dns/cache-domains.txt)

# A request for a cached CDN domain is handled by the lancache cache (X-LanCache header)
curl -s -D - -o /dev/null -H "Host: $CDN" http://127.0.0.1/lancache-heartbeat | grep -iE 'HTTP/|X-LanCache'

# An arbitrary HTTP Host is refused - the connection is closed, so curl exits non-zero
curl -s -o /dev/null -H 'Host: example.com' http://127.0.0.1/ ; echo "arbitrary HTTP Host -> curl exit $? (non-zero / refused = not an open proxy)"

# An arbitrary TLS SNI relays no upstream certificate
CERTS=$(timeout 8 openssl s_client -connect 127.0.0.1:443 -servername example.com </dev/null 2>/dev/null | grep -c 'BEGIN CERTIFICATE')
echo "arbitrary TLS SNI -> certificates relayed: $CERTS (0 = not an open TLS relay)"

A cached domain handled by the lancache nginx while an arbitrary HTTP Host is refused and an arbitrary TLS SNI relays zero certificates

Step 6: Point your network at the cache

To start caching, point your clients' DNS at this VM. The appliance answers only the cached CDN domains (with its own IP) and refuses everything else, so it is not a general-purpose resolver. Two common ways to use it:

  • Conditional forwarding (recommended): on your existing DNS server or router, forward only the lancache CDN domains to this VM, and keep your normal resolver for everything else. This keeps general browsing working through your usual DNS.
  • Per-client: set the DNS server on the machines you want to cache to this VM's IP, and add a secondary DNS for general name resolution.

The per-VM appliance info file records this VM's IP and how to use it. The container images are pinned by digest.

sudo cat /root/lancache-credentials.txt
sudo docker images --digests --format '{{.Repository}} {{.Digest}}' | grep lancachenet
df -h /

The per-VM appliance info file, the pinned digest-verified lancache images, and the OS disk holding the cache

Once clients resolve a cached CDN through this VM, their first download of a given file fills the cache (a MISS); every subsequent machine that downloads the same file gets it from the cache at LAN speed (a HIT). No client-side software is needed — the caching is transparent.

Step 7: Sizing and expanding the cache

Cache data lives under /var/lib/lancache on the OS disk, which ships at 64 GiB. A game or content cache can grow large, so for anything beyond casual use, give it more storage:

  • Resize the OS disk in the Azure Portal (VM → Disks → the OS disk → Size) and reboot; Ubuntu grows the root filesystem automatically.
  • Or attach a data disk, format and mount it at /var/lib/lancache, then reboot so first boot re-populates the cache directory on the larger volume.

The cache manages its own size (it evicts the least-recently-used objects to stay within its configured limit and keeps a minimum amount of free disk), so it will not fill the disk completely.

Troubleshooting

  • A client is not being cached. Confirm the client's DNS is actually pointed at this VM and that the CDN domain is in the allow-list: dig @<vm-ip> <cdn-domain> should return the cache IP. Check the NSG allows UDP+TCP 53 from the client.
  • Downloads fail after pointing DNS here. Ensure ports 80 and 443 are open in the NSG to your clients. HTTPS CDNs need 443; HTTP CDNs need 80.
  • Container logs: sudo docker logs lancache-monolithic and sudo docker logs lancache-dns.
  • Restart the stack: sudo docker restart lancache-monolithic lancache-dns.

Support

cloudimg provides 24/7 support with a guaranteed 24 hour response SLA for all Azure Marketplace images. For assistance with this image, contact support through the cloudimg listing on Azure Marketplace or visit www.cloudimg.co.uk.