Deployment guide

LanCache 2026.10.09

Overview

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

This guide covers launching and verifying LanCache 2026.10.09 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 guide hub of the listing describes the other VPN, proxy and remote access servers.

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

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 address of the cache, which sends clients 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).

What you get:

  • Docker CE 29.9.2 from the signed apt repository of Docker, running the two official LanCache images, each pinned by digest:
    • lancachenet/monolithic built 2026-10-09
    • lancachenet/lancache-dns built 2026-09-27, with one SecureAnyCloud change: Ubuntu 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) and lancache-acl (manages which networks may use the cache)

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

Security posture

An internet-reachable DNS server and HTTP proxy is a classic abuse target. Attackers can use it 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 the private IP of this instance. Every other name gets REFUSED. BIND 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 default configuration of the upstream image, 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
    • the client signature of Steam 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 the CIDR blocks of its VPC 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. The DOCKER-USER chain of Docker 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 own services of the host. 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
  • Nothing per-instance ships. No container exists in the image, because the containers are created on your instance. The per-host identifiers of Docker, the client list, the DNS zone and the cache sizing are all created on the first boot of your instance. 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 the repository of Docker (no universe packages).

Prerequisites

  • An AWS account and permission to launch EC2 instances
  • An EC2 key pair in the Region you launch in
  • A subnet with outbound internet access (the cache fetches from the CDNs on the first download of each file)
  • Clients that can reach the private IP of the instance: 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

Connect to LanCache 2026.10.09 on Ubuntu 24.04 over SSH. The operating system is Ubuntu 24.04 LTS, and the SSH user is ubuntu.

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.
  • When the subscription is active, choose Launch your software.
  • Choose Launch from EC2 Console. In Version, choose LanCache 2026.10.09 on Ubuntu 24.04, then choose your Region.
  • Pick an instance type (m5.large is recommended, and the software charge is $0.04 per vCPU per hour) and your key pair.
  • 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).
  • Security group: the default group allows only SSH (22). Add the following rules, with your VPC CIDR (or LAN CIDR) as the source and never 0.0.0.0/0:
    • UDP 53 and TCP 53 (DNS)
    • TCP 80 (the cache)
    • TCP 443 (the HTTPS pass-through)
  • Choose a subnet with internet access and leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.

Option B: AWS CLI

Run the following on your own workstation. Replace the AMI ID with the ID shown for your Region on the Marketplace launch page. Replace 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 a token even before the firewall rules apply. The firewall blocks the metadata service in either case.

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 private IP and VPC CIDR blocks of the instance, writes the client list, the DNS zone and the cache size, and then starts both containers. The instance information file holds no secret:

sudo cat /root/lancache-info.txt
# LanCache on Ubuntu 24.04 (sac) - 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 private IP of the instance. Your clients use this address for both DNS and the cache.

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 (the nginx status page) is not published. It is reachable only inside the container.

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. An internet reachability check runs alongside, 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
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 the only DNS server of a client. Forward just the CDN names to LanCache from the DNS that 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. Every non-CDN name fails 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 that the documented Steam integration of LanCache relies on. Once the name resolves to the cache, the Steam client sends its content downloads there. The cache accepts the Steam client on the own CDN host names of Steam only.

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 the CIDR block(s) of your VPC:

sudo lancache-acl list
# LanCache clients (sac): 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. Also allow the range in the security group:

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

lancache-acl updates the client list of BIND 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.

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)
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

These are the rules that block 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.

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 the free space of that file system, 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, choose one of the following:

  • 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 run the commands that follow.

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 (to 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 that the cache uses to reach the CDNs. NOFETCH=true makes the cache use the pinned CDN list on the image instead of downloading the list from GitHub on every start.

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 the request 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 the IP addresses of your clients and the files they download. The messages of the DNS server go to the journal (sudo journalctl -u lancache-dns).

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/sac/data/lancache-ready exists. Before each start, lancache-preflight refuses to continue unless all of the following hold:
    • the pinned image is present
    • its configuration exists
    • the container firewall (metadata DROP, client list, host guard) is loaded
12

Updates

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

The DNS image still contains the upstream 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 entry point of the container is BIND, and the CDN list is copied out of the image without starting it.

13

Security recommendations

  • Open ports 53, 80 and 443 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 all your clients are 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 license texts, the exact image digests and commits, and the Dockerfile of the DNS image are in /usr/share/doc/sac-lancache/. Docker CE is Apache-2.0. BIND and nginx inside the images carry their own licenses.
  • SecureAnyCloud provides 24/7 technical support by email and live chat: sales@secureanycloud.com.
  • LanCache, Steam, Epic Games, Battle.net, Xbox, PlayStation, Nintendo and Windows are trademarks of their respective owners. SecureAnyCloud is not affiliated with or endorsed by any of them.

Deploy on AWS

Launch LanCache 2026.10.09 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