LanCache NG is a local download cache for your home network, LAN party, lab, school, office or gaming room.
It stores game and software downloads inside your local network.
The first download still comes from the internet.
The next download can come from your local cache at LAN speed.
This is useful when multiple PCs download the same games, updates or drivers.
It can reduce internet traffic, save bandwidth and make repeated downloads much faster.
LanCache NG is still actively changing.
The current setup already provides the main stack, guided installation, Admin UI, DNS based cache routing, optional SSL caching, optional DHCP, optional LanCache-NG-NTP server, optional scheduled automatic updates, and secondary DNS support.
Some internal paths, root elements and service details may still change while the project grows.
The Rust coverage badge above is a Shields endpoint backed by coverage/rust-master.json on the repository's badges branch -- this README is only ever viewed on master (GitHub always renders the default branch's README on the repo homepage), so the badge here specifically tracks master's own coverage rather than whatever integration branch happens to be active. On trusted branch pushes, the rust_coverage/publish_coverage_badge jobs write the latest measured services/ui and services/dns/nats-subscriber percentages to a branch-scoped file (coverage/rust-<branch>.json, so master and the active vX.Y.Z integration branch don't overwrite each other's numbers); pull requests measure and gate coverage but do not publish badge JSON. The same coverage job runs cargo tarpaulin against both Rust crates and enforces a per-crate threshold: services/ui must stay at or above 35% (real measured coverage is ~38.6% as of this writing), and services/dns/nats-subscriber's threshold is still set at 0% in the workflow. Issue #504 (closed via PR #515) added real unit tests for dns_record_to_zone_update()'s subscribe/forward logic, extracted as a pure, testable function -- so the crate's tests are no longer data-model-only -- but the rust_coverage job's threshold was deliberately left at 0% pending a real tarpaulin measurement, and nothing has raised it since. Each crate's threshold is raised independently as that crate gains real coverage.
LanCache NG combines several services into one Docker based stack:
- DNS service
- for redirecting known CDN domains to your local cache
- Nginx cache proxy for standard HTTP caching
- optional SSL cache proxy for HTTPS capable clients
- Admin UI for cache status, domains, DNS records, DHCP leases and settings
- optional Kea DHCP server
- optional DHCP-Dnsmasq-based proxy helper
- optional LanCache-NG-NTP server (chrony), disciplined against public NTP servers, serving LAN clients on UDP/123, with an opt-in DHCP auto-populate toggle
- optional scheduled automatic updates (ordered, health-gated, Admin UI last)
- optional watchdog and convergence checks
- optional secondary DNS nodes synced through NATS
The goal is simple:
Your clients use LanCache NG as DNS server.
Known download domains are answered with the cache IP.
The cache proxy downloads and stores the requested files.
Later clients can receive the same files from your LAN.
Run this on a Linux machine inside your network:
curl -fsSL https://raw.githubusercontent.com/wiki-mod/lancache-ng/master/setup.sh | sudo bashThe setup script guides you through the installation. If required tools are missing, it asks before installing packages, installs missing requirements such as Docker, curl and git through the host package manager, and prints the package names to install manually if you abort. It is a pull-only installer: it resolves and pulls prebuilt first-party images, so Redis-backed sccache, distcc and other build accelerators do not affect normal setup or update paths.
It can:
- check the required tools
- ask before installing missing dependencies such as Docker, curl or git
- clone the repository to
/opt/lancache-ng - ask for the cache server IP
- optionally enable SSL caching with a second IP
- ask for cache directory and cache size
- optionally enable scheduled automatic updates (a host systemd timer)
- create pre-update rollback backups
- create config-only or full backups
- restore setup-script backups
- optionally enable DHCP
- optionally protect the Admin UI with a password
- write the
.envfile - create required directories
- start the Docker stack
- install systemd startup and convergence checks
Default installation path:
/opt/lancache-ng
After setup, open the Admin UI:
http://<server-ip>:8080
Example:
http://192.168.1.10:8080
LanCache NG can run in two modes.
Standard mode is the safest and easiest mode.
It does not need a certificate on the clients.
Use this mode if you want maximum compatibility.
Standard mode:
- caches HTTP downloads
- passes HTTPS traffic through normally
- works without client certificate setup
- works well for PCs, consoles and general devices
- is the recommended first test mode
Example DNS target:
192.168.1.10
SSL mode is optional.
It can cache HTTP and HTTPS downloads, but it needs a locally generated Root CA certificate on every client that should use SSL caching.
Use this mode only for devices you own and trust.
SSL mode:
- needs a second LAN IP
- needs the generated CA certificate installed on each client
- can improve cache hit rates for launchers and CDNs using HTTPS
- is useful for Windows gaming PCs
- is not suitable for most consoles
Example DNS target:
192.168.1.11
Both modes can run at the same time.
Example:
| Mode | Client setup | DNS server |
|---|---|---|
| Standard mode | no certificate needed | 192.168.1.10 |
| SSL mode | CA certificate installed | 192.168.1.11 |
This lets you decide per client.
A console can use standard mode.
A gaming PC with the certificate installed can use SSL mode.
LanCache NG can cache many game, software and update downloads when the required domains are known.
Examples:
- Steam
- Epic Games
- GOG
- EA and Origin
- Blizzard and Battle.net
- Ubisoft
- Riot Games
- Rockstar Games
- Warframe
- Path of Exile
- Guild Wars 2
- Windows Update
- Microsoft Office
- NVIDIA drivers
- AMD drivers
- Linux package mirrors
Important:
Not every platform behaves the same.
Some services change their CDN domains.
Some downloads use HTTPS in a way that only works with SSL mode.
Some clients may bypass the cache completely.
The first download is normally a cache miss.
The second identical download is where the cache should help.
LanCache NG is not a generic forward proxy. It is designed to force caching for known game, software and update CDN downloads that you intentionally route through the cache.
The nginx cache key intentionally uses the host and path, not the full request URI with the query string. Many CDN download URLs include per-request signatures or expiry tokens in the query string. Including those values in the cache key would make each signed URL look like a different object and would greatly reduce cache hits. On a cache miss (or once the cached entry has expired), the full request URI including the query string is forwarded to the upstream CDN for validation. Cache hits within the configured validity window are served directly from the local cache without contacting upstream.
For the same reason, LanCache NG intentionally ignores selected upstream cache headers such as
Cache-Control, Expires, Vary and Set-Cookie for cached download responses. Do not use
LanCache NG as a general-purpose proxy, and only add CDN domains that you understand and want
to cache.
Xbox, PlayStation and similar consoles should usually use standard mode.
They normally cannot install a custom Root CA certificate in a useful way.
Because of that, SSL caching is not recommended for consoles.
Consoles should continue to work normally through standard mode.
HTTPS traffic that cannot be cached is passed through to the original CDN.
You need:
- a Linux machine that stays online
- Docker with the Docker Compose plugin
- a static LAN IP for the cache server
- enough disk space for the cache
- port
53free for DNS - port
80free for HTTP cache traffic - port
443free if SSL mode is enabled - port
8080for the Admin UI - optional second LAN IP for SSL mode
- optional port
67/udpif using the built in DHCP server
Recommended storage:
| Environment | Suggested cache size |
|---|---|
| small test setup | 50 GB |
| few gaming PCs | 100 GB |
| LAN party or shared network | 500 GB or more |
A fast SSD is nice, but a large HDD can also work.
The best choice depends on your network, internet speed and number of clients.
Recommended installation:
curl -fsSL https://raw.githubusercontent.com/wiki-mod/lancache-ng/master/setup.sh | sudo bashDuring setup you will be asked for the important values. If required tools are missing, setup asks before installing packages and prints the package names to install manually if you abort.
Example values:
| Setting | Example |
|---|---|
| Standard mode IP | 192.168.1.10 |
| SSL mode IP | 192.168.1.11 |
| install directory | /opt/lancache-ng |
| cache directory (shared) | /opt/lancache-ng/cache |
| cache size | 500 GiB |
| Admin UI port | 8080 |
After setup, check the containers:
cd /opt/lancache-ng
docker compose psOpen the Admin UI:
http://<standard-ip>:8080
If you installed with the setup script, update with:
sudo /opt/lancache-ng/setup.sh updateThe update command can:
- create a config-only rollback backup before changing the stack
- pull the current repository state
- update the compose file
- pull newer images
- restart every service except the Admin UI first, verify the whole non-UI set is actually healthy, then restart the Admin UI last
- automatically roll back to the pre-update backup if that health check fails
Setup and update always consume prebuilt runtime images. They do not build the production stack locally, so host-side build acceleration is never a runtime or install dependency.
Instead of running setup.sh update manually, a host systemd timer
(lancache-auto-update.timer) can run the same ordered, health-gated update
on a schedule. Enable it at install time (the "Scheduled automatic updates"
prompt, default: disabled), later by setting AUTO_UPDATE_ENABLED=1 in .env
and re-running setup.sh, or from the Admin UI's Setup page (release channel
and scheduled-update toggle). A change made from the Admin UI is picked up by
the host's convergence timer (currently every 5 minutes), not instantly --
that control's own copy says so. It only actually pulls and restarts when the
selected release channel has moved to a new image set -- an unchanged channel
is a silent no-op, not a full restart on every tick. Manually trigger the same
check with:
sudo /opt/lancache-ng/setup.sh auto-updateUpdate validation: The update command validates your configuration during the process. If required config values are missing or invalid, the update will abort immediately rather than continuing with a partial or degraded configuration. If this happens, check that your .env and config/*/ files exist and contain all required keys. See docs/backup-restore.md for rollback options.
Create a manual backup with:
sudo /opt/lancache-ng/setup.sh backup --config
sudo /opt/lancache-ng/setup.sh backup --fullUse --config for small update rollback backups. Use --full only when you also need cache objects, because full backups can be very large. Restore a backup with:
sudo /opt/lancache-ng/setup.sh restore /var/backups/lancache-ng/lancache-ng-config-YYYYMMDDTHHMMSSZ.tar.gz /opt/lancache-ngSee docs/backup-restore.md for backup scope, restore testing, secret handling, CA lifecycle notes, and rollback details.
LANCACHE_IMAGE_CHANNEL selects the first-party image channel. setup.sh
resolves mutable channels to the immutable LANCACHE_IMAGE_TAG used by Docker
Compose. The interactive installer asks which channel to use, defaulting to
stable.
stable(default) is the channel promoted after the full release validation gate.LANCACHE_IMAGE_CHANNEL=latestis the same channel under its original name — both resolve to the identical published image.nightlyis the most recently built, less tested pre-stable channel, built fromcurrent_dev's tip once a day plus on-demand (renamed fromedgein v0.3.0; corrected from "published on every current_dev push" to this once-daily/on-demand model in #1254/#1255). Opt in only if you want the newest changes.vX.Y.Zpins all stack services to an immutable stable release tag.- Branch and commit images are optional for development and testing.
If CI has published them, valid examples are branch names (for branch pushes)
and short
sha-<short>tags.
Recommended for production:
- Use
latestfor normal stable deployments. - Use a tagged release value (for example
v1.2.3) for pinned deployments. - Use
nightly, branch tags, orsha-*tags only for temporary test environments where intentional drift is acceptable.
The release workflow publishes service images with branch, tag, and SHA tags and keeps release source notes in GitHub releases.
The curl | bash one-liner above self-clones /opt/lancache-ng from
origin's default branch (master) when run standalone with no local repo
present. LANCACHE_IMAGE_CHANNEL already lets you pin which image channel
gets pulled, but until now there was no equivalent for which git ref the
installer bootstraps its own on-disk checkout from. Set
LANCACHE_SETUP_GIT_REF to a branch, tag, or commit-ish to bootstrap from
that ref instead, for example to validate a pre-release branch the same
documented way:
curl -fsSL https://raw.githubusercontent.com/wiki-mod/lancache-ng/master/setup.sh | sudo LANCACHE_SETUP_GIT_REF=v0.2.0 bashUnset (the default) keeps today's behavior unchanged: the installer tracks
origin's default branch, both for a fresh clone and when re-run against an
existing /opt/lancache-ng checkout.
If something does not work, run:
sudo /opt/lancache-ng/setup.sh debugThe debug command prints:
- container status
- recent logs
- cache usage
- detected LAN IPs
- health check results
You can also view live logs manually:
cd /opt/lancache-ng
docker compose logs -fIf you need to open a GitHub issue for something that isn't working, run:
sudo /opt/lancache-ng/setup.sh create-logs-for-issueThis bundles everything a maintainer needs to triage the report into one compressed, timestamped archive and prints its path:
docker compose logs/ps/configoutput- a copy of
.env/.env.localwith every credential-shaped value (API keys, TSIG keys, passwords, tokens) redacted - host facts (Docker/Compose versions, kernel, disk space)
- known-good-snapshot directory listings (file names only, no content) --
see
docs/known-good-config-snapshots.md
Review the archive yourself before attaching it to an issue -- this command never uploads or attaches anything automatically.
LanCache NG works by DNS.
A client must use the LanCache NG DNS IP.
You can set that manually per client or distribute it through DHCP.
Example:
| Client type | DNS server |
|---|---|
| normal client without certificate | 192.168.1.10 |
| SSL client with certificate installed | 192.168.1.11 |
On many routers you can set the DNS server handed out by DHCP.
Set it to the LanCache NG standard IP if you want all clients to use standard mode.
For SSL mode, only point clients to the SSL IP after the CA certificate was installed.
LanCache NG only caches traffic for clients that resolve CDN hostnames through
its DNS servers. DHCP is the most reliable way to hand those DNS servers to
clients. setup.sh lets you pick one of four DHCP modes (stored as
DHCP_MODE in .env):
disabled— LanCache NG does not manage or proxy DHCP. You point clients at its DNS yourself (router DHCP option or static client config). This is the right starting point for most users.kea— LanCache NG runs a full Kea DHCP server that hands out IP address, gateway, and the correct cache DNS servers, and supports reservations and lease management in the Admin UI. Use this only when you are ready to make LanCache NG the network's DHCP server — configure and confirm Kea is running here first, then disable your router's built-in DHCP server, not the other way around (see docs/dhcp-modes.md for why the order matters).dnsmasq-proxy— LanCache NG runs dnsmasq in proxy-DHCP mode next to an existing DHCP server for networks where the router or ISP gateway keeps DHCP enabled and cannot be disabled. It does not own leases and is limited to proxy/PXE clients; it does not reliably replace the DNS option handed out by a normal router DHCP server.dnsmasq-relay— LanCache NG runs dnsmasq as a real DHCP relay, forwarding each client's DHCP request to an upstream DHCP server on another network segment and relaying the reply back. Use this when clients and their DHCP server are on different subnets/VLANs and the client broadcasts can't reach the server directly. It injects nothing of its own — the upstream server owns the whole lease.
The four modes are mutually exclusive — Kea and dnsmasq both bind DHCP port
67/udp, so setup activates exactly one, and switching modes in the Admin UI
stops the other service. The two dnsmasq modes (dnsmasq-proxy and
dnsmasq-relay) share one container and are also mutually exclusive.
Important: do not run two normal DHCP servers in the same network unless you planned it carefully. If your router already provides DHCP, either keep using the router (and set DNS another way) or switch DHCP fully to LanCache NG.
See docs/dhcp-modes.md for when to use each mode, what is
not available in dnsmasq-proxy mode, how to set the upstream DHCP IP, and how
to verify clients actually receive the LanCache NG DNS servers.
LanCache-NG-NTP is a small, self-contained NTP server (chrony) you can enable
from the Admin UI's NTP page. It disciplines its own clock against a curated
set of public NTP servers (never operating as a standalone/undisciplined time
source) and serves that time to LAN clients on 123/udp. The upstream server
list is editable from the same page.
A separate "auto-set this as the DHCP NTP server" toggle, off by default, only
takes effect when both LanCache-NG-NTP and the Kea DHCP server are enabled: it
pushes this container's LAN address into Kea's ntp-servers DHCP option for
every subnet, so clients no longer need a manually-typed third-party NTP
server address. Turning the container on does not enable this by itself —
the DHCP auto-populate toggle is always a separate, explicit choice.
The Admin UI is available after setup:
http://<server-ip>:8080
The Admin UI can be used for:
- cache overview
- cache fill level
- hit and miss statistics
- active downloads
- domain management
- LAN DNS records
- DHCP leases
- NTP settings
- settings
- secondary DNS management
If the setup asks whether the Admin UI should be password protected, using a password is recommended.
Do not expose the Admin UI to the internet.
SSL mode generates a local CA certificate.
Clients using SSL mode must trust this certificate.
After the first start, the certificate is available here:
/opt/lancache-ng/certs/ca.crt
Depending on your installation path, the file may be inside your selected install directory.
Use PowerShell as Administrator:
scripts\install-ca-cert.ps1Use:
sudo scripts/install-ca-cert.shSee:
docs/install-ca-cert.md
docs/backup-restore.md
Firefox can use its own certificate store.
If HTTPS caching does not work in Firefox, import the certificate into Firefox too.
Steam Deck can require extra handling because the system is more locked down than a normal Linux desktop.
See the certificate documentation before using SSL mode on it.
On a client using LanCache NG as DNS server, run:
nslookup steamcontent.comor specify the DNS server directly:
nslookup steamcontent.com 192.168.1.10If DNS routing works, known CDN domains should resolve to your cache IP instead of a public CDN IP.
A simple test:
- Point a client to the LanCache NG DNS server.
- Download a supported game or update.
- Delete it or try the same download from another client.
- Download it again.
- Check whether the second download is faster.
- Check the Admin UI for cache activity.
The first download normally goes to the internet.
The second download is the important test.
You can manage domains in the Admin UI.
This is the easiest option and usually does not require a rebuild.
Manual domain file:
services/dns/cdn-domains.txt
One list drives everything: DNS based cache routing, and (as of v0.2.0) SSL mode certificate coverage too. The proxy derives each entry's registrable root domain automatically at startup and generates a wildcard cert for it — there is no separate SSL domain file to keep in sync by hand.
After manual file changes in a repository-based setup, restart the affected services so they reload the mounted files:
cd /opt/lancache-ng
docker compose restart dns-standard dns-ssl proxyOnly add domains you understand.
Wrong domains can break downloads or route traffic that should not be cached.
LanCache NG can run additional DNS nodes on other machines.
This is useful if:
- you have more than one network area
- you want DNS redundancy
- you want another host to answer DNS requests
- you want DNS closer to some clients
Secondary DNS nodes sync with the primary through NATS.
Prerequisite: the primary must be configured with a NATS address a remote
secondary can actually reach before you register one. By default NATS is not
published on the host at all, so registration fails closed with HTTP 503
until the primary has NATS_BIND_IP (reusing the same trusted LAN/VPN
interface docker-compose.nats-secondary.yml binds NATS to) or, for a
non-default port/scheme/hostname, the more specific NATS_ADVERTISE_URL
set in its .env/.env.local, and the nats service recreated with the
docker-compose.nats-secondary.yml override included so it actually
publishes that address. See
docs/architecture-ng.md's "Remote secondary NATS access"
section for the full setup and the exact failure modes it fail-closes.
Setup example:
bash <(curl -fsSL https://raw.githubusercontent.com/wiki-mod/lancache-ng/master/setup.sh) secondary \
--primary http://192.168.1.10:8080 \
--token <SECONDARY_REGISTRATION_TOKEN> \
--name my-secondary \
--proxy-ip 192.168.1.10The registration token is generated during primary setup and stored in .env.
You can also view a ready command inside the Admin UI under the secondary DNS section.
If a secondary was created before scoped NATS user/password credentials existed,
rerun the same command with --rotate in that secondary's install directory.
This rewrites the local secondary .env from the primary registration API
without changing the secondary name.
After setup, point additional clients to the secondary DNS IP.
The setup script is recommended.
Manual setup is useful if you want to inspect and control every file yourself.
Clone the repository:
cd /opt
git clone https://github.com/wiki-mod/lancache-ng.git
cd lancache-ngCopy the checked-in production template to an untracked runtime env file and edit that file:
cp deploy/prod/.env deploy/prod/.env.local
nano deploy/prod/.env.localSet at least:
IP_STANDARD=192.168.1.10
IP_SSL=192.168.1.11
SSL_ENABLED=1
LANCACHE_STATE_DIR=/opt/lancache-ng
# Optional cache directory override. Leave unset unless you intentionally place
# the cache on a separate disk; compose derives it from LANCACHE_STATE_DIR.
# CACHE_DIR=/opt/lancache-ng/cache
CACHE_MAX_SIZE=50g
CACHE_MEM_MB=512
PROXY_SECURITY_MODE=lazy
PROXY_ALLOWED_CLIENT_CIDRS=
CACHE_MAX_GB=50
# First-party service image selector.
# stable is the channel promoted after the full release validation gate
# (latest is the same channel under its original name -- both resolve
# identically). Use nightly only when you explicitly want the most recently
# built, less tested pre-stable channel (formerly called edge).
# setup.sh resolves mutable channels to an immutable sha-* image tag before pull.
# Do not change LANCACHE_IMAGE_TAG by hand unless LANCACHE_IMAGE_CHANNEL=pinned.
LANCACHE_IMAGE_REGISTRY=ghcr.io
LANCACHE_IMAGE_PREFIX=wiki-mod/lancache-ng
LANCACHE_IMAGE_CHANNEL=stable
LANCACHE_IMAGE_TAG=sha-<resolved-by-setup>NGINX_UPSTREAM_RESOLVER is what nginx uses to resolve the real CDN hostnames it proxies to. It is not read from deploy/prod/.env.local — deploy/prod/docker-compose.yml's proxy service only loads it via env_file: ../../config/prod/proxy.env, with no ${NGINX_UPSTREAM_RESOLVER} interpolation in that service's environment: list, so an override placed in .env.local has no effect on this variable. Edit config/prod/proxy.env directly, then recreate the proxy container (docker compose --env-file deploy/prod/.env.local -f deploy/prod/docker-compose.yml up -d proxy) to pick up the change. Because config/prod/proxy.env is a tracked file (unlike .env.local), a manual edit to it can conflict with the repository pull during the upgrade flow described below if a future release also touches that file — the pre-pull config backup in that flow covers this file too, so follow those steps (don't edit and pull outside of them) and reapply your resolver override afterward if it was reset. The default there — 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844] (Google Public DNS, both IPv4 and IPv6; IPv6 entries are bracketed because nginx's resolver directive requires that) — is a convenience default, not a requirement. Set it to whatever real upstream DNS servers you prefer: your ISP's resolvers, a corporate DNS server, 1.1.1.1, or anything else. The actual requirement is that the chosen resolver returns the CDN's real origin address for these hostnames — not merely that its own server IP differs from the LanCache DNS/proxy IP. Setup rejects the obvious case (setting this directly to IP_STANDARD/IP_SSL) automatically, but it cannot detect an indirect loop: for example, a split-DNS or conditional-forwarding resolver that itself forwards the CDN's zone back to the LanCache DNS server would still hand nginx the LanCache proxy IP, at which point nginx loops even though the configured resolver's own IP isn't the cache's. If you use split-DNS or conditional forwarding upstream of this resolver, make sure CDN zones aren't among the forwarded ones.
PROXY_SECURITY_MODE controls how defensive the proxy is at request time:
lazyis the default and keeps the traditional LanCache-style behavior: if a client reaches the cache, nginx proxies the requested host upstream. This is the deliberate cache-first choice so new CDN hostnames keep working out of the box.strictNOT RECOMMENDED! Only proxies hosts whose root domain is derived fromservices/dns/cdn-domains.txt; unknown hosts receive403 Forbidden. This reduces accidental or abusive proxying, but it can AND will break downloads until the missing CDN hostname is added tocdn-domains.txt— there is no separate file to maintain, but you still need to add every CDN hostname you want to cache by hand.
PROXY_ALLOWED_CLIENT_CIDRS can optionally restrict who may use the proxy, for example 192.168.1.0/24 172.16.0.0/12. Leave it empty to allow any client that can reach the bound LAN/Docker ports; setup.sh writes the empty value by default for the normal LAN-only deployment model where firewalling and Docker port bindings already define the boundary.
KEEP_KNOWN_GOOD_CONFIGS (default 3) controls how many validated nginx/dnsmasq/PowerDNS configurations are kept for automatic rollback if a newly generated config fails validation at container startup. It also controls how many known-good Kea DHCP config snapshots the Admin UI keeps for operator-selected rollback (see the DHCP settings page). See docs/known-good-config-snapshots.md.
LANCACHE_IMAGE_CHANNEL controls the mutable stack channel. stable (default)
and latest both mean the same released channel — stable is the friendlier
name setup.sh's interactive picker offers, latest is the original name and
remains valid. Use nightly only when you explicitly want the most recently
built, less tested pre-stable channel (renamed from edge in v0.3.0).
setup.sh resolves mutable channels
through the stack pointer image and writes the immutable LANCACHE_IMAGE_TAG
that Docker Compose pulls. If you install from a tagged release archive or a
checked-out vX.Y.Z / vX.Y.Z-rc.N tag, set LANCACHE_IMAGE_CHANNEL=pinned
and LANCACHE_IMAGE_TAG to that same release tag so the running containers
match the source tree.
LANCACHE_IMAGE_REGISTRY and LANCACHE_IMAGE_PREFIX select where first-party images are pulled from. Keep the defaults for GHCR, or point both values at a private mirror that provides the complete stack package set.
The resulting install/update path still stays pull-only and does not depend on
local compiler caches or remote compiler services.
Current prebuilt first-party images are published for both linux/amd64 and linux/arm64, built natively on GitHub-hosted arm64 runners rather than emulated -- see docs/self-hosted-actions-runner.md.
Release channels and package rules are documented in docs/release-versioning.md. External image handling is documented in docs/release-external-images.md.
Keep deploy/prod/.env as the checked-in template. Manual production changes
belong in deploy/prod/.env.local, which is ignored by git and preferred by
setup.sh update, setup.sh auto-update, setup.sh backup, setup.sh restore,
setup.sh update-ip, and setup.sh create-logs-for-issue when present.
If you use NATS, secondary DNS or DHCP DDNS, set real secret values too:
DDNS_TSIG_KEY=<generate-a-secret>
NATS_UI_PASSWORD=<generate-a-secret>
NATS_DNS_WRITER_PASSWORD=<generate-a-secret>
NATS_DNS_REPLICA_PASSWORD=<generate-a-secret>
NATS_CALLOUT_PASSWORD=<generate-a-secret>
NATS_SYS_PASSWORD=<generate-a-secret>SECONDARY_REGISTRATION_TOKEN is different from the values above: it is
always required, even on a single-node install that never registers a
secondary DNS node. The Admin UI's boot-time check refuses to start on an
empty value or on the checked-in CHANGE_ME_SECONDARY_REGISTRATION_TOKEN
placeholder (an empty token previously allowed unauthenticated secondary
registration — see issue #659), so replace it with a real secret regardless
of whether you plan to use secondary DNS:
SECONDARY_REGISTRATION_TOKEN=<generate-a-secret>Secrets in .env follow a simple rule: they are generated once on first install (or first update), and never rotated by later updates as long as a real value already exists. The setup.sh script provides a get_or_generate_secret helper that checks each secret key:
- If the value is a placeholder (empty or the literal string
<generate-a-secret>), a new secret is generated. - If a real value is already present, it is preserved unchanged — even across multiple updates.
Secret formats vary by key:
*_KEYsecrets (e.g.DDNS_TSIG_KEY): 32 hex characters*_PASSWORDsecrets (e.g.NATS_UI_PASSWORD): 32 base64 charactersSECONDARY_REGISTRATION_TOKEN: 20 alphanumeric characters
Store your actual secret values securely if you need to rotate them manually. The setup script will not regenerate them unless you explicitly delete or clear the .env entry.
Create directories:
mkdir -p /opt/lancache-ng/cache
mkdir -p /opt/lancache-ng/kea
mkdir -p /opt/lancache-ng/pdns-standard
mkdir -p /opt/lancache-ng/pdns-ssl
mkdir -p /opt/lancache-ng/pdns-filter-state
mkdir -p /opt/lancache-ng/nats
mkdir -p /opt/lancache-ng/nats-confStart the stack:
docker compose --env-file deploy/prod/.env.local -f deploy/prod/docker-compose.yml pull
docker compose --env-file deploy/prod/.env.local -f deploy/prod/docker-compose.yml up -dFor later upgrades from this manual checkout, keep local changes in
deploy/prod/.env.local, create a rollback backup before pulling new repository
files, then update the production stack directory explicitly instead of using a
raw compose pull/up:
sudo ./setup.sh backup --config "$(pwd)/deploy/prod"
git pull --ff-only
sudo ./setup.sh update "$(pwd)/deploy/prod"The explicit pre-pull backup preserves the currently working compose,
.env.local, certificates and runtime config before tracked files change. The
update command creates another rollback backup after the pull, applies
state-path migrations and validates compose before restarting the stack.
Check status:
docker compose --env-file deploy/prod/.env.local -f deploy/prod/docker-compose.yml psLanCache NG can run inside a Proxmox LXC container.
Recommended:
- use a Debian based unpriviledged container
- use enough disk space
- enable nesting
- keep the container inside your LAN
- give the container a static IP
- use a second IP if SSL mode is enabled
Example LXC config on the Proxmox host:
features: nesting=1,keyctl=1
lxc.apparmor.profile: unconfined
For unprivileged containers and port 53, this can be needed on the host:
echo 'net.ipv4.ip_unprivileged_port_start=53' >> /etc/sysctl.conf
sysctl -pIf SSL mode is enabled, the container requires a second usable LAN IPs.
- The reason is, that you then be able to cache also SSL Downloads from the configured domain list.
- But you will need to install the CA-Certificate to let this happen.
| Port | Protocol | Used for |
|---|---|---|
53 |
TCP and UDP | DNS |
80 |
TCP | HTTP cache |
443 |
TCP | SSL mode cache |
8080 |
TCP | Admin UI |
67 |
UDP | optional DHCP server |
8000 |
TCP | optional Kea control agent |
123 |
UDP | optional LanCache-NG-NTP server |
Keep these ports inside your LAN.
Do not expose them directly to the internet.
Important paths:
deploy/quickstart/ Quickstart compose used by setup.sh
deploy/prod/ Production compose files
deploy/secondary/ Secondary DNS compose files
docs/ Documentation
scripts/ Helper scripts
services/dns/ DNS service
services/proxy/ Unified proxy for HTTP mode and SSL mode
services/ui/ Admin UI
services/watchdog/ Watchdog service
services/dhcp/ Kea DHCP service
services/dhcp-proxy/ DHCP proxy service
services/ntp/ LanCache-NG-NTP (chrony) service
services/nats/ NATS sync service
certs/ Generated or mounted certificates
Check that the container is running:
cd /opt/lancache-ng
docker compose psCheck whether port 53 is already used by another service:
ss -tulpen | grep ':53'Common causes:
- another DNS server is already using port
53 - systemd resolved is listening on port
53 - the client is not using the LanCache NG DNS IP
- firewall rules block DNS
- Docker is not running
This can be normal for the first download.
Check:
- is the client using the correct DNS server
- is the domain part of the cache domain list
- is the download using HTTPS
- if SSL mode is needed, is the client using the SSL DNS IP
- if SSL mode is needed, is the CA certificate installed
Check:
- the client uses the SSL mode DNS IP
- the second IP is really assigned to the server
- the CA certificate is installed
- Firefox has the certificate if Firefox is used
- the domain exists in the SSL domain list
- the SSL proxy container is running
Check:
cd /opt/lancache-ng
docker compose ps
docker compose logs --tail=100 uiCommon causes:
- wrong IP
- blocked port
8080 - UI container not running
- firewall blocks access
- manual production setup binds UI differently than quickstart setup
A low hit rate can happen when:
- everything is downloaded for the first time
- every client downloads different files
- the platform changed CDN domains
- the download uses unsupported HTTPS behavior
- the domain is missing from the domain list
- the cache size is too small and old files are removed
LanCache NG is intended for trusted local networks.
Recommended:
- do not expose DNS, proxy or Admin UI to the internet
- protect the Admin UI with a password
- keep generated tokens private
- keep the CA private
- install the CA certificate only on clients you own and trust
- do not use SSL mode for unknown or guest devices
- review the setup script before running it on important systems
A local Root CA can inspect traffic for configured domains.
That is required for SSL caching, but it also means the CA must be handled carefully.
Use standard mode when:
- you want the easiest setup
- you use consoles
- you do not want to install certificates
- you only want safe basic caching
- you are testing the project for the first time
Use SSL mode when:
- you control the client device
- you can install the CA certificate
- you want better cache coverage
- you understand that HTTPS caching needs trust in the local CA
- you are using supported game launchers on PCs
AGPL-3.0 — see LICENSE. Chosen specifically because this project is a network server: the AGPL closes the "SaaS loophole" plain GPL leaves open, so anyone running a modified version of lancache-ng as a network service (not just distributing binaries) must also make that modified source available to the service's users.
