Networking AWS

LanCache 2026.10.09 on AWS User Guide

| Product: LanCache 2026.10.09 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and verifying LanCache 2026.10.09 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.

LanCache is the open-source download cache from the LanCache.NET project. It keeps one copy of every game or update file a client downloads, so the next client on your network gets it from the cache instead of the internet. Two parts work together:

  • The cache (lancachenet/monolithic): an nginx HTTP cache for the content CDNs of Steam, Epic Games, Battle.net, Riot, Xbox, PlayStation, Nintendo, Windows Update, Office and more, plus a pass-through on port 443 for the HTTPS-only CDNs.
  • The DNS server (lancachenet/lancache-dns): it answers those CDN names with the cache's address, which is how clients are sent to the cache without any client-side setting.

The list of cached CDN names is the community-maintained uklans/cache-domains list, as bundled in the pinned LanCache DNS image (120 domain names across 26 services).

LanCache has no version numbers of its own: 2026.10.09 is the build date of the official lancachenet/monolithic image this AMI runs.

What is on the image:

  • Docker CE 29.9.2 from Docker's own signed apt repository, running the two official LanCache images, each pinned by digest:
  • lancachenet/monolithic built 2026-10-09
  • lancachenet/lancache-dns built 2026-09-27, with cloudimg's one change: Ubuntu's package updates applied (apt-get upgrade, which fixes OpenSSL CVE-2026-84782)
  • lancache-dns.service and lancache-cache.service: systemd units that run the two containers
  • lancache-firstboot.service: per-instance setup on first boot
  • lancache-netguard.service: the container firewall
  • Helpers: lancache-selftest (health and security self-test), lancache-acl (manage which networks may use the cache)

Default user: ubuntu · Architecture: x86_64 · Root volume: 16 GiB gp3 (the cache lives on it; see Step 9 to grow it)

Security posture

An internet-reachable DNS server and HTTP proxy is a classic abuse target: it can be used for DNS amplification, as an open proxy, or to reach services inside your VPC. This image closes each of those paths:

  • Not an open resolver. BIND answers only the cached CDN names, each with this instance's private IP. Every other name gets REFUSED. It never recurses or forwards, has no control channel, rate-limits its answers, and runs as an unprivileged user with no Linux capabilities and a read-only file system. The upstream image's default configuration, an open forwarding resolver, is never used.
  • Not an open proxy. The cache refuses any request whose target host is not on the CDN list (nginx 444: the connection is closed with no response). The check runs before nginx picks a route, and it covers every way a request can name a host:
  • the Host header
  • a full URL in the request line
  • a look-alike name such as lancache.steamcontent.com.example.com
  • Steam's client signature sent with any other host

On port 443 only listed CDN names are passed through. * Only your networks can use it. On first boot the instance reads its VPC's CIDR blocks from the instance metadata service and writes them to /etc/lancache/allowed-clients.txt. A firewall chain in front of the containers drops connections from every other source, and BIND accepts queries only from the same list. lancache-acl adds an on-premises LAN (Step 6). It refuses 0.0.0.0/0 and any range wider than /8. * No path from the containers to the metadata service, your VPC or the instance itself. Docker's DOCKER-USER chain drops container traffic to 169.254.169.254 (instance metadata) and 168.63.129.16. The cache can open connections only to public internet addresses and to the Amazon DNS resolver (169.254.169.253). Private, VPC, link-local and loopback ranges are dropped, and so are connections to the host's own services. dockerd and containerd are denied the metadata service too. The AMI is registered IMDSv2 only. * Hardened Docker. The Docker daemon has: * no TCP API socket and no registry credentials * published ports handled in the kernel (no userland proxy) * no inter-container traffic * no-new-privileges, and core dumps off * size-rotated container logs

No container exists in the image: the containers are created on your instance. * Nothing per-instance ships. Docker's per-host identifiers, the client list, the DNS zone and the cache sizing are all created on your instance's first boot. LanCache has no login and no password, so there is no credential to leak. * Fail closed. The containers start only after first boot has written its configuration and the container firewall is loaded. If either is missing, nothing listens. * Patched. The OS is fully patched at build time and unattended-upgrades stays on. Every installed package comes from Ubuntu main or Docker's repository (no universe packages).

Prerequisites

  1. An AWS account and permission to launch EC2 instances
  2. An EC2 key pair in the Region you launch in
  3. A subnet with outbound internet access (the cache fetches from the CDNs on the first download of each file)
  4. Clients that can reach the instance's private IP: instances in the same VPC, or an office or LAN connected over a VPN or AWS Direct Connect

Recommended instance type: m5.large. LanCache is light on CPU and memory; disk is what matters, because the cache can only hold as much as its volume.

Connecting to your instance

Version Operating system SSH user
LanCache 2026.10.09 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu

Step 1: Launch the instance

Option A: AWS Marketplace console

  1. 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.
  2. Once the subscription is active, choose Launch your software.
  3. Choose Launch from EC2 Console. In Version, choose LanCache 2026.10.09 on Ubuntu 24.04, then choose your Region.
  4. Pick an instance type (m5.large recommended; the software charge is $0.04 per vCPU per hour) and your key pair.
  5. Storage: give the root volume the size you want the cache to be, plus about 6 GiB. For example, 200 GiB holds about 155 GiB of cached downloads. You can also attach a separate data volume later (Step 9).
  6. Security group: the default group allows only SSH (22). Add these rules, with your VPC CIDR (or LAN CIDR) as the source, never 0.0.0.0/0:
  7. UDP 53 and TCP 53 (DNS)
  8. TCP 80 (the cache)
  9. TCP 443 (the HTTPS pass-through)
  10. Choose a subnet with internet access, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.

Option B: AWS CLI

Run this on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and address ranges with your own:

SG=$(aws ec2 create-security-group --group-name lancache \
  --description "LanCache download cache" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22  --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 53  --cidr <your-vpc-or-lan-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 53  --cidr <your-vpc-or-lan-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80  --cidr <your-vpc-or-lan-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr <your-vpc-or-lan-cidr>
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 \
  --block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=200,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpPutResponseHopLimit=1 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=lancache}]'

HttpPutResponseHopLimit=1 keeps IMDSv2 tokens on the host itself, so a container cannot obtain one even before the firewall rules apply. The firewall blocks the metadata service either way.

Step 2: Connect and read the instance information

Connect as ubuntu with the key pair you chose:

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

First boot finishes within a minute of launch. It reads the instance's private IP and VPC CIDR blocks, writes the client list, the DNS zone and the cache size, then starts both containers. The instance information file holds no secret:

sudo cat /root/lancache-info.txt
# LanCache on Ubuntu 24.04 (cloudimg) - generated on first boot. There is NO login and NO credential.
LANCACHE_IP=172.31.92.11
LANCACHE_DNS_SERVER=172.31.92.11
LANCACHE_HEARTBEAT_URL=http://172.31.92.11/lancache-heartbeat
LANCACHE_ALLOWED_CLIENTS=172.31.0.0/16
# Point your clients' DNS at 172.31.92.11: it answers ONLY the game/OS-update CDN names LanCache caches (REFUSED for every
# other name), so keep your normal resolver as the secondary or forward only those names to this instance.
# Open UDP+TCP 53, TCP 80 and TCP 443 in the security group to your VPC/LAN CIDR only. Guide: see the MOTD.

LANCACHE_IP is the instance's private IP: the address your clients use for both DNS and the cache.

Step 3: Confirm LanCache is running

systemctl is-active docker.service lancache-netguard.service lancache-firstboot.service lancache-dns.service lancache-cache.service
active
active
active
active
active
sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
NAMES                 IMAGE          STATUS                        PORTS
lancache-monolithic   155817065b13   Up About a minute (healthy)   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 8080/tcp
lancache-dns          e8f1784f8732   Up About a minute             0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp

Port 8080 (nginx's status page) is not published; it is reachable only inside the container.

Step 4: Run the self-test

lancache-selftest checks that the cache and DNS work, and that each closed path is still closed:

  • arbitrary names get REFUSED
  • arbitrary hosts, full-URL requests, look-alike names and the Steam-signature trick are refused
  • an arbitrary TLS name is not passed through
  • the containers cannot reach the instance metadata service (with an internet reachability check, so a broken network cannot fake a pass)
sudo lancache-selftest
SELFTEST_OK: heartbeat 204; DNS maps the CDN names to 172.31.92.11 and REFUSES others; arbitrary Host, regex-location, absolute-form, suffix and Steam-UA requests closed (444); arbitrary SNI not relayed; containers cannot reach instance metadata

Step 5: Point your clients at the cache

Clients use LanCache by resolving the CDN names to it. LanCache answers only those names and refuses everything else, so it cannot be a client's only DNS server. Forward just the CDN names to it from the DNS your clients already use:

  • Route 53 Resolver: a forwarding rule per name in /etc/lancache/domains.txt, with LANCACHE_IP as the target.
  • Active Directory DNS: conditional forwarders.
  • Unbound, dnsmasq or Pi-hole: forward-zone or server entries for the same names.

Everything else keeps resolving as before. To try one client quickly, set its DNS server to LANCACHE_IP and expect every non-CDN name to fail on that client.

Check the answers from the instance itself:

dig +short @127.0.0.1 lancache.steamcontent.com
dig +short @127.0.0.1 download.epicgames.com
172.31.92.11
172.31.92.11

Any other name is refused, with no answer:

dig @127.0.0.1 example.com | grep -E 'status|ANSWER:'
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 22711
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

The cached services and names:

python3 -c "import json; print(', '.join(s['name'] for s in json.load(open('/var/lib/lancache/cachedomains/cache_domains.json'))['cache_domains']))"
grep -c . /etc/lancache/domains.txt
arenanet, blizzard, bsg, cityofheroes, cod, daybreak, epicgames, frontier, neverwinter, nexusmods, nintendo, origin, pathofexile, renegadex, riot, rockstar, sony, square, steam, teso, test, uplay, warframe, wargaming, wsus, xboxlive
120

Steam is handled through lancache.steamcontent.com, the name LanCache's documented Steam integration relies on: once it resolves to the cache, the Steam client sends its content downloads there. The cache accepts Steam's client on Steam's own CDN host names only.

Step 6: Choose which networks may use the cache

Only the ranges in /etc/lancache/allowed-clients.txt can query the DNS server or use the cache. First boot writes your VPC's CIDR block(s):

sudo lancache-acl list
# LanCache clients (cloudimg): DNS :53 and the cache :80/:443 answer ONLY these IPv4 ranges.
# First boot wrote this instance's VPC CIDR block(s). Add an on-premises LAN with: sudo lancache-acl add <CIDR>
172.31.0.0/16

To serve an office LAN that reaches the VPC over a VPN or Direct Connect, add its range. Remember to allow it in the security group too:

sudo lancache-acl add 192.168.10.0/24
sudo lancache-acl remove 192.168.10.0/24

lancache-acl updates BIND's client list and the container firewall in one step, and restarts the DNS container. It refuses 0.0.0.0/0 and any range wider than /8.

Step 7: Watch a download being cached

Request a small Office file through the cache twice. The first request is fetched from the CDN (MISS). Every later request, from any client, is served from the cache (HIT):

for i in 1 2; do curl -s -o /dev/null -D - -H 'Host: officecdn.microsoft.com' http://127.0.0.1/pr/wsus/ofl.cab | grep -iE '^HTTP|X-Upstream-Cache-Status'; done
HTTP/1.1 200 OK
X-Upstream-Cache-Status: MISS
HTTP/1.1 200 OK
X-Upstream-Cache-Status: HIT

A host that is not on the list is refused (the connection is closed, so curl reports 000):

curl -s -o /dev/null -w 'example.com via the cache: HTTP %{http_code}\n' -m 10 -H 'Host: example.com' http://127.0.0.1/ || echo "the cache closed the connection (curl exit $?)"
example.com via the cache: HTTP 000
the cache closed the connection (curl exit 52)

Step 8: Metadata and VPC access is blocked for the containers

From inside the cache container, an IMDSv2 token request times out (curl exit code 28):

sudo docker exec lancache-monolithic curl -s -m 5 -o /dev/null -w 'HTTP %{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token; echo "curl exit $?"
HTTP 000
curl exit 28

The rules that do it:

sudo iptables -S DOCKER-USER
sudo iptables -S LANCACHE-GUARD | tail -4
-N DOCKER-USER
-A DOCKER-USER -d 169.254.169.254/32 -j DROP
-A DOCKER-USER -d 168.63.129.16/32 -j DROP
-A DOCKER-USER -j LANCACHE-GUARD
-A LANCACHE-GUARD -d 240.0.0.0/4 -i docker0 -j DROP
-A LANCACHE-GUARD -d 172.31.0.0/16 -i docker0 -j DROP
-A LANCACHE-GUARD -s 172.31.0.0/16 -o docker0 -j RETURN
-A LANCACHE-GUARD -o docker0 -j DROP

The last three lines are the client list: the cache may not open connections into the VPC, connections from the VPC are let through, and every other source is dropped. lancache-netguard loads these rules before Docker starts, and again after any Docker restart.

Step 9: Cache storage and size

Cached files live under /var/lib/lancache/cache on the root volume. First boot sizes the cache to 80% of that file system's free space, less 1 GiB:

df -h /var/lib/lancache/cache | tail -1
grep -v '^#' /etc/lancache/lancache.env
/dev/root        15G  4.3G   11G  30% /
CACHE_DISK_SIZE=7353m
CACHE_INDEX_SIZE=250m
MIN_FREE_DISK=1g
UPSTREAM_DNS=169.254.169.253
NOFETCH=true

To give the cache more room, either:

  • Grow the root volume. Modify the EBS volume in the console, then reboot (cloud-init grows the partition and the file system at boot) or run sudo growpart /dev/nvme0n1 1 && sudo resize2fs /dev/nvme0n1p1.
  • Mount a dedicated data volume at /var/lib/lancache/cache. Attach the volume, then:

text lsblk -o NAME,SIZE,SERIAL # the new volume's SERIAL is its volume ID without the dash sudo systemctl stop lancache-cache sudo mkfs.ext4 /dev/nvme1n1 echo "UUID=$(sudo blkid -s UUID -o value /dev/nvme1n1) /var/lib/lancache/cache ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab sudo mount /var/lib/lancache/cache

Then raise CACHE_DISK_SIZE in /etc/lancache/lancache.env (about 80% of the new space) and run sudo systemctl restart lancache-cache. The cache container takes ownership of the new file system when it starts. UPSTREAM_DNS is the Amazon DNS resolver the cache uses to reach the CDNs. NOFETCH=true makes the cache use the pinned CDN list on the image instead of downloading it from GitHub on every start.

Step 10: Logs

Cache access logs are in /var/lib/lancache/logs. They rotate daily and seven days are kept. Each line names the service, the client address, the request and whether it was a cache HIT or MISS:

sudo tail -n 2 /var/lib/lancache/logs/access.log
[wsus] 172.17.0.1 / - - - [27/Oct/2026:15:03:05 +0000] "GET /pr/wsus/ofl.cab HTTP/1.1" 200 19047 "-" "curl/8.5.0" "HIT" "officecdn.microsoft.com" "-"
[example.com] 172.17.0.1 / - - - [27/Oct/2026:15:03:05 +0000] "GET / HTTP/1.1" 444 0 "-" "curl/8.5.0" "-" "example.com" "-"

These logs contain your clients' IP addresses and the files they download. The DNS server's messages go to the journal (sudo journalctl -u lancache-dns).

Step 11: How the fail-closed start works

  • lancache-firstboot.service runs once, before both container units. If it cannot read the private IP or the VPC CIDR from the instance metadata service, it writes nothing, neither container starts, and the next boot tries again.
  • lancache-dns.service and lancache-cache.service start only when /var/lib/cloudimg/lancache-ready exists. Before each start, lancache-preflight refuses to continue unless:
  • the pinned image is present
  • its configuration exists
  • the container firewall (metadata DROP, client list, host guard) is loaded

Step 12: Updates

  • Ubuntu packages: unattended-upgrades applies Ubuntu security updates automatically.
  • Docker CE: Docker's apt repository stays enabled, so sudo apt-get update && sudo apt-get upgrade takes Docker's updates.
  • LanCache images: the two images are pinned by digest and are not updated in place. cloudimg ships newer LanCache builds as new versions in the listing.

The DNS image still contains upstream's dnstool config generator, built with Go 1.27.1, which carries four Go standard-library advisories fixed in Go 1.27.3. This image never runs dnstool: the container's entry point is BIND, and the CDN list is copied out of the image without starting it.

Step 13: Security recommendations

  • Keep ports 53, 80 and 443 open in the security group to your VPC or LAN ranges only. The client list already drops other sources, but the security group is the first wall.
  • Restrict SSH (22) to your management address.
  • Keep the cache in a private subnet if your clients are all inside AWS. It needs outbound internet access (a NAT gateway is enough), not inbound.
  • Size the volume for your catalogue: a full modern game can be 100 GiB or more.

Support and licensing

  • LanCache (lancachenet/monolithic, lancachenet/lancache-dns) and the uklans/cache-domains list are MIT-licensed. The licence texts and the exact image digests, commits and the DNS image's Dockerfile are in /usr/share/doc/cloudimg-lancache/. Docker CE is Apache-2.0. BIND and nginx inside the images carry their own licences.
  • cloudimg provides 24/7 technical support by email and live chat: support@cloudimg.co.uk.
  • LanCache, Steam, Epic Games, Battle.net, Xbox, PlayStation, Nintendo and Windows are trademarks of their respective owners. cloudimg is not affiliated with or endorsed by any of them.