A web UI for LANCache. Watch downloads as they happen, see what's already cached, track bandwidth saved, and prefill Steam and Epic games before your next LAN party.
Important
Always pull the latest tag. GitHub's package page surfaces :dev because dev builds publish more often, but :dev is for testing only and can break at any time.
docker pull ghcr.io/regix1/lancache-manager:latest- Screenshots
- Quick Start
- Docker Compose
- Configuration
- Prefill (Steam & Epic)
- Custom Themes
- Multiple Datasources
- Reverse Proxy (Nginx)
- Monitoring (Grafana & Prometheus)
- Troubleshooting
- Building from Source
- Contributing Translations
- Support and License
Stats at a glance with draggable cards, service analytics, and top clients
Three view modes to browse your cached games
Monitor which devices are using your cache
Calendar view of download activity and LAN events
Pick between Steam and Epic to start a prefill session
Game selection, download settings, and real-time activity log
Authentication, demo mode, and display preferences
Log processing, corruption detection, and game cache detection
Browse installed themes or create your own with the theme editor
Assign friendly names to client IPs and exclude clients from stats
Run the container, point it at your existing LANCache logs and cache, and you're online in under a minute.
docker run -d \
--name lancache-manager \
-p 8080:80 \
-v ./data:/data \
-v /path/to/lancache/logs:/logs:ro \
-v /path/to/lancache/cache:/cache:ro \
-e TZ=America/Chicago \
-e LanCache__LogPath=/logs/access.log \
-e LanCache__CachePath=/cache \
ghcr.io/regix1/lancache-manager:latestThen:
-
Grab your API key from the container logs:
docker logs lancache-manager | grep "API Key"
It's also written to
/data/security/api_key.txt. -
Open
http://localhost:8080. -
Head to Management, paste your API key, and click Process Logs to import your existing cache history.
For anything beyond a quick test, use Compose. Here's the minimum that gets you running:
services:
lancache-manager:
image: ghcr.io/regix1/lancache-manager:latest
container_name: lancache-manager
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./data:/data
- /mnt/lancache/logs:/logs:ro
- /mnt/lancache/cache:/cache:ro
- /var/run/docker.sock:/var/run/docker.sock # Optional: for prefill and log rotation
environment:
- PUID=33
- PGID=33
- TZ=America/Chicago
- LanCache__LogPath=/logs/access.log
- LanCache__CachePath=/cacheDrop :ro from the /cache mount if you want to clear cache or remove individual games from the UI. The Docker socket is optional — you only need it for nginx log rotation and prefill (Steam or Epic).
The full compose file with every available knob, all commented out:
services:
lancache-manager:
image: ghcr.io/regix1/lancache-manager:latest
container_name: lancache-manager
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./data:/data
- /mnt/lancache/logs:/logs:ro
- /mnt/lancache/cache:/cache:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
# Required
- PUID=33
- PGID=33
- TZ=America/Chicago
- LanCache__LogPath=/logs/access.log
- LanCache__CachePath=/cache
- ASPNETCORE_URLS=http://+:80
# Security
# - Security__EnableAuthentication=true
# - Security__MaxAdminDevices=3
# - Security__GuestSessionDurationHours=6
# - Security__RequireAuthForMetrics=false
# - Security__ProtectSwagger=true
# - Security__AllowedOrigins=
# - Security__AllowedBrowsePaths=
# Prefill (Steam & Epic)
# - Prefill__DockerImage=ghcr.io/regix1/steam-prefill-daemon:latest
# - Prefill__EpicDockerImage=ghcr.io/regix1/epic-prefill-daemon:latest
# - Prefill__SessionTimeoutMinutes=120
# - Prefill__DaemonBasePath=/data/prefill
# - Prefill__HostDataPath=auto
# - Prefill__NetworkMode=auto
# - Prefill__LancacheDnsIp=auto
# - Prefill__UseTcp=auto
# - Prefill__TcpPort=4379
# - Prefill__HostTcpPort=4379
# - Prefill__TcpHost=127.0.0.1
# Nginx Log Rotation
# - NginxLogRotation__Enabled=true
# - NginxLogRotation__ContainerName=auto
# - NginxLogRotation__ScheduleHours=24
# API Options
# - ApiOptions__MaxClientsPerRequest=1000
# - ApiOptions__DefaultClientsLimit=100
# Optimization
# - Optimizations__EnableGarbageCollectionManagement=false
# Paths & Datasources
# - LanCache__EnvFilePath=/lancache/.env
# - LanCache__AutoDiscoverDatasources=true
# Debugging
# - Logging__LogLevel__LancacheManager.Infrastructure.Platform=Debug| Volume | Purpose | Notes |
|---|---|---|
/data |
PostgreSQL database, security, state and config, themes, cached images | Required |
/logs |
LANCache access logs | Add :ro for read-only |
/cache |
LANCache cached files | Add :ro to monitor without touching files |
/var/run/docker.sock |
Docker API access | Optional. Needed for nginx log rotation and Steam/Epic prefill |
| Variable | Default | Description |
|---|---|---|
PUID |
33 |
User ID the app runs as. Match the owner of your cache and log files. |
PGID |
33 |
Group ID the app runs as. |
TZ |
UTC |
Timezone for log timestamps (e.g., America/Chicago). TimeZone is also accepted as a fallback. |
LanCache__LogPath |
— | Path inside the container to the LANCache access log. |
LanCache__CachePath |
— | Path inside the container to the LANCache cache directory. |
LANCache Manager stores everything in PostgreSQL. There are two ways to set the password:
- Set
POSTGRES_PASSWORDin compose. The app picks it up automatically and skips the setup screen. Best for automation. - Leave it unset. On first launch the UI shows a setup page where you enter the password in the browser.
| Variable | Default | Description |
|---|---|---|
POSTGRES_PASSWORD |
— | PostgreSQL password. |
POSTGRES_USER |
lancache |
PostgreSQL username. |
If you're upgrading from an older SQLite build, the migration runs automatically on first start — your downloads, settings, and cached data carry over without any manual steps.
Compose example with the password baked in:
services:
lancache-manager:
image: ghcr.io/regix1/lancache-manager:latest
container_name: lancache-manager
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./data:/data
- /mnt/lancache/logs:/logs:ro
- /mnt/lancache/cache:/cache:ro
- /var/run/docker.sock:/var/run/docker.sock
environment:
- PUID=33
- PGID=33
- TZ=America/Chicago
- LanCache__LogPath=/logs/access.log
- LanCache__CachePath=/cache
- POSTGRES_PASSWORD=your-secure-password
# - POSTGRES_USER=lancache # Optional, defaults to "lancache"| Variable | Default | Description |
|---|---|---|
Security__EnableAuthentication |
true |
Require an API key for admin actions. Only turn off for local dev. |
Security__MaxAdminDevices |
3 |
How many devices can share the same API key. |
Security__GuestSessionDurationHours |
6 |
Default guest session length (also configurable in the UI). |
Security__RequireAuthForMetrics |
false |
Require an API key on /metrics. |
Security__ProtectSwagger |
true |
Require auth on Swagger docs in production. |
Security__AllowedOrigins |
(empty) | Comma-separated CORS allow list. Empty allows all. |
Security__AllowedBrowsePaths |
(empty) | Comma-separated allow-list of file-browser roots. Empty disables the file browser entirely (every request returns 403). Example: /data,/mnt. |
Security__ApiKeyPath |
/data/security/api_key.txt |
Override the file path the admin API key is read from and written to. Useful if you bind-mount secrets from outside /data. |
Security__KnownProxyNetworks |
(empty) | Comma-separated CIDR list of trusted proxy networks for X-Forwarded-For (e.g. 172.16.0.0/12,10.0.0.0/8). Set this when nginx, Traefik, or another reverse proxy fronts the manager so client IPs are reported correctly. Loopback is always trusted. |
Security__TrustAllProxies |
false |
Trust every upstream proxy unconditionally. Convenient for local dev. Never enable on an internet-exposed host — anyone can spoof a client IP. |
| Level | What you can do | Examples |
|---|---|---|
| Admin | Everything. Requires the API key. | Clear cache, process logs, change settings |
| Guest | Read-only views. Requires admin auth or a guest session. | Browse downloads, stats, events, client data |
To hand out a guest link without sharing your API key, open the Users tab and click Create Guest Link. Guests can browse the dashboard but can't change anything. Nothing is public — every endpoint requires either admin auth or a valid guest session.
The DeveLanCacheUI import flow uses an admin-only file browser endpoint (/api/filebrowser/list) to find a .db file on the server. For safety, the browser is disabled by default — without an explicit allow-list, every request returns 403 and you'll see this in the log:
warn: LancacheManager.Controllers.FileBrowserController[0]
FileBrowser: Security:AllowedBrowsePaths is not configured. All file-browse requests will be rejected (403) until paths are configured.
To turn it on, set Security__AllowedBrowsePaths to a comma-separated list of directories the browser is allowed to enter. Subdirectories of each root are fine; everything outside the list returns 403.
environment:
- Security__AllowedBrowsePaths=/data,/mntIf you don't use the DeveLanCacheUI importer, leave it empty. The warning is informational — nothing else in the app cares.
Most of these are auto-detected. The only one you typically need to set by hand is Prefill__NetworkMode, and only if the auto-detection guesses wrong for your Docker setup. See Prefill (Steam & Epic) for the full network setup guide.
| Variable | Default | Description |
|---|---|---|
Prefill__DockerImage |
ghcr.io/regix1/steam-prefill-daemon:latest |
Docker image used for Steam prefill containers. |
Prefill__EpicDockerImage |
ghcr.io/regix1/epic-prefill-daemon:latest |
Docker image used for Epic prefill containers. |
Prefill__SessionTimeoutMinutes |
120 |
Inactive session cleanup timeout. |
Prefill__DaemonBasePath |
/data/prefill |
Where session data is stored. |
Prefill__HostDataPath |
auto |
Host path mapping to /data. Auto-detected from container mounts. |
Prefill__NetworkMode |
auto |
Network mode for prefill containers: host, bridge, a network name, or auto. |
Prefill__LancacheDnsIp |
auto |
IP of your lancache-dns container. Auto-detected from Docker. |
Prefill__UseTcp |
auto |
Use TCP for daemon communication. auto resolves to true on Windows, false on Linux. |
Prefill__TcpPort |
4379 |
TCP port inside the prefill container. |
Prefill__HostTcpPort |
4379 |
TCP port exposed on the host for prefill containers. |
Prefill__TcpHost |
127.0.0.1 |
TCP host used by the prefill daemon. |
| Variable | Default | Description |
|---|---|---|
LanCache__EnvFilePath |
(auto) | Path to the lancache .env file (used to read CACHE_DISK_SIZE). Searches common locations if unset. |
LanCache__AutoDiscoverDatasources |
false |
Auto-detect datasources from matching subdirectories under /cache and /logs. |
If you run more than one cache instance or split services across drives, see Multiple Datasources.
| Variable | Default | Description |
|---|---|---|
NginxLogRotation__Enabled |
true |
Tell nginx to reopen its logs after the app rotates them. Requires the Docker socket. |
NginxLogRotation__ContainerName |
auto |
LANCache container name. auto finds containers with "lancache" in the name. |
NginxLogRotation__ScheduleHours |
24 |
How often to check whether rotation is needed. |
| Variable | Default | Description |
|---|---|---|
ApiOptions__MaxClientsPerRequest |
1000 |
Max clients returned in a single stats request. |
ApiOptions__DefaultClientsLimit |
100 |
Default client limit when none is provided. |
Optimizations__EnableGarbageCollectionManagement |
false |
Show memory management controls in Management. Helpful on low-memory hosts. |
ASPNETCORE_URLS |
http://+:80 |
Internal port binding. Don't change unless you know exactly why. |
Prefill downloads games into your cache before people connect. When guests show up, every install reads from your cache instead of the public internet — full LAN speed, no bandwidth bottleneck.
Steam and Epic each run in their own container, so you can prefill both at the same time without them interfering. Progress streams live to the UI.
The flow is the same for both services:
- Open the Prefill tab and pick Steam or Epic Games
- Sign in (Steam Guard for Steam, OAuth for Epic)
- Pick games from your library
- Hit Start
That's it. Leave it running — when guests arrive, everything's cached.
Note
Steam prefill is built on steam-prefill-daemon, a fork of steam-lancache-prefill by @tpill90. Epic prefill uses epic-prefill-daemon.
Have a list of App IDs from steam-lancache-prefill or somewhere else? Skip the library browser:
- Click Select Apps
- Click Import App IDs
- Paste your IDs in any of these formats:
- Comma-separated:
730, 570, 440 - JSON array:
[730, 570, 440] - One per line
- Comma-separated:
- Click Import
The dialog tells you how many games were added, how many were already selected, and how many IDs aren't in your Steam library (those are skipped at prefill time).
Tip
Coming from steam-lancache-prefill? Open selectedAppsToPrefill.json and paste the contents straight into the import field — the JSON array is parsed as-is.
- Docker socket mounted (
/var/run/docker.sock) - Logged in as admin in lancache-manager
- Your cache server is reachable from the prefill container (see Network setup below)
Most installs need zero config. If you run the standard lancache + lancache-dns containers, lancache-manager auto-detects them and prefill works out of the box.
If your DNS isn't a stock lancache-dns (you use AdGuard Home, Pi-hole, public DNS, etc.) or your routing is unusual, set one env var and you're done.
| Your setup | What to set |
|---|---|
Stock lancache + lancache-dns containers |
nothing |
| Single-box install (lancache on the same host as lancache-manager) | nothing |
| AdGuard Home, Pi-hole, or any DNS replacement | Prefill__LancacheIp=<your-cache-ip> |
| Host networking, host's DNS doesn't route CDN to your cache | Prefill__LancacheIp=<your-cache-ip> |
Caddy/Squid/non-nginx cache that routes by Host: header |
Prefill__LancacheIp=<your-cache-ip> |
| You want predictable behavior regardless of environment | always set Prefill__LancacheIp |
Tip
Prefill__LancacheIp is the universal escape hatch. When set, prefill talks to your cache by IP and never asks DNS where the cache lives. Network mode and DNS server settings stop mattering for CDN traffic.
| Variable | What it does |
|---|---|
Prefill__NetworkMode |
host or bridge. Defaults to auto-detect from your lancache-dns container. |
Prefill__LancacheIp |
IP (or hostname) of your lancache cache server (the HTTP server holding cached files, port 80). Forwarded to the daemon as LANCACHE_IP. The daemon connects to this IP directly with a spoofed Host: header — no DNS lookup. Works in both host and bridge mode. |
Prefill__LancacheDnsIp |
IP of your DNS server (lancache-dns, AdGuard, Pi-hole — port 53). Written into the container's /etc/resolv.conf so the daemon queries it for hostname lookups. Used in bridge mode only — Docker silently drops DNS overrides on host-network containers. |
Important
LancacheIp and LancacheDnsIp are different services, even on the same machine. Quick mental model:
| What it is | Port | Job | |
|---|---|---|---|
LancacheIp |
The cache server (lancachenet/monolithic, or any HTTP cache) |
HTTP / 80 | Holds the actual cached game files |
LancacheDnsIp |
The DNS server (lancachenet/lancache-dns, AdGuard Home, Pi-hole, etc.) |
DNS / 53 | Translates lancache.steamcontent.com into the cache's IP |
Think of a small town. The cache is the library (where the books live). The DNS server is the information booth (where you ask "which way to the library?"). Both can sit in the same building — same IP, different ports — but they do completely different jobs. You can't borrow a book from the information booth, and the librarian doesn't give directions.
When you set LancacheIp, the daemon skips the information booth entirely and walks straight to the library. That's why it's the universal escape hatch: DNS becomes irrelevant for cache traffic.
Important
Steam (api.steampowered.com) and Epic (*.epicgames.com) auth and manifest endpoints still use normal DNS. LANCACHE_IP only redirects CDN chunk traffic — the only domains lancache caches. Your login and metadata traffic is unaffected.
Most reliable — LancacheIp makes CDN routing DNS-independent:
environment:
- Prefill__NetworkMode=host
- Prefill__LancacheIp=192.168.1.10Bridge mode with a non-standard DNS (e.g., AdGuard Home replacing lancache-dns):
environment:
- Prefill__NetworkMode=bridge
- Prefill__LancacheIp=192.168.1.10 # cache server
- Prefill__LancacheDnsIp=192.168.1.20 # DNS serverBridge mode, stock lancache-dns, no IP override (legacy DNS-driven path):
environment:
- Prefill__NetworkMode=bridge
- Prefill__LancacheDnsIp=192.168.1.20Tip
Prefill container has no internet? Try Prefill__NetworkMode=bridge. Some Docker setups block outbound traffic in host mode.
Each prefill session runs a connectivity test on startup and writes the result to logs:
═══════════════════════════════════════════════════════════════════════
PREFILL CONTAINER NETWORK DIAGNOSTICS - prefill-daemon-abc123
═══════════════════════════════════════════════════════════════════════
Internet connectivity: OK (reached api.steampowered.com)
lancache.steamcontent.com resolved to 192.168.1.10
DNS looks correct (private IP - likely your lancache server)
═══════════════════════════════════════════════════════════════════════
If the resolved IP is a public address (Steam's real CDN IPs look like 162.254.x.x), traffic is bypassing your cache. Set Prefill__LancacheIp and restart the session.
For when you want to know exactly which path a request takes:
flowchart TD
Start([Prefill needs a game chunk from<br/>lancache.steamcontent.com])
LIP{Did you set<br/>Prefill__LancacheIp?}
Start --> LIP
LIP -->|YES| Direct[Talk to that IP directly,<br/>no DNS lookup needed.<br/>Host header tells the cache<br/>which CDN to serve.]
Direct --> OK([Hits your cache every time])
LIP -->|NO| DNS[Daemon asks DNS:<br/>where does lancache.steamcontent.com<br/>actually live?]
DNS --> Mode{Which NetworkMode<br/>is the container in?}
Mode -->|host| HostDNS[Container uses whatever DNS<br/>the host machine uses.<br/>Prefill__LancacheDnsIp can't help —<br/>Docker silently drops it in host mode.]
Mode -->|bridge| BridgeDNS{Did you set<br/>Prefill__LancacheDnsIp?}
BridgeDNS -->|YES| ForcedDNS[Container queries<br/>that DNS server]
BridgeDNS -->|NO| AutoDetect[Daemon falls back to its<br/>own auto-detect chain:<br/>CDN name, localhost,<br/>then the default gateway]
HostDNS --> Outcome{Did DNS return<br/>your cache server's IP?}
ForcedDNS --> Outcome
AutoDetect --> Outcome
Outcome -->|YES| OK
Outcome -->|NO| Public([Got the real CDN's public IP —<br/>traffic skips your cache])
style OK fill:#1f883d,color:#fff
style Public fill:#cf222e,color:#fff
style Direct fill:#0969da,color:#fff
Every combination, in one table:
NetworkMode |
LancacheIp |
LancacheDnsIp |
Outcome |
|---|---|---|---|
host |
set | (any) | Reliable. LANCACHE_IP injected; DNS irrelevant. |
host |
unset | (any) | Risky. DnsIp is silently dropped (Docker limitation). Works only if the host's DNS already resolves CDN to your cache. |
bridge |
set | unset | Reliable. LANCACHE_IP injected; DNS irrelevant. |
bridge |
set | set | Reliable. LANCACHE_IP for CDN, DnsIp for auth/manifest. |
bridge |
unset | set | Works if DnsIp resolves CDN to your cache. Container DNS forced to DnsIp. |
bridge |
unset | unset | Inconsistent. Auto-detect probes localhost/gateway. Works for some setups. |
Why LancacheIp always works: with the env var set, the daemon builds requests like GET http://192.168.1.10/depot/123/chunk/abc with Host: lancache.steamcontent.com. Your cache (nginx, Caddy, or any HTTP server that routes by Host:) sees the right hostname and serves from cache. DNS is never consulted for the CDN domain.
Open Management > Preferences > Theme Management to:
- Build themes from scratch with a live preview
- Browse and install community themes
- Import and export themes as TOML
Themes live in /data/themes/. Here's the minimum format:
[meta]
name = "My Theme"
id = "my-theme"
isDark = true
version = "1.0.0"
author = "Your Name"
[colors]
primaryColor = "#3b82f6"
bgPrimary = "#111827"
textPrimary = "#ffffff"Most people run a single LANCache instance and never touch this section. You only need it if you've split services across cache directories or you run more than one LANCache server and want them combined in a single dashboard.
A "datasource" is a paired log + cache directory. Each one is processed and tracked separately, then aggregated in the dashboard and downloads views.
Common reasons to use it:
- Outsourced services — Steam lives on a separate drive from everything else.
- Multiple LANCache instances — separate cache servers for different rooms or purposes.
- Segmented storage — different services on different partitions.
Point the app at the parent directories and let it scan:
environment:
- LanCache__LogPath=/logs
- LanCache__CachePath=/cache
- LanCache__AutoDiscoverDatasources=trueTwo things get detected:
- Root-level datasource — if
/logs/access.logexists and/cachecontains LANCache hash directories (00/,01/, etc.), it creates a "Default" datasource. - Subdirectory datasources — for each folder that exists in both
/cacheand/logs, it creates a named datasource (e.g.,/cache/steam+/logs/steambecomes "Steam").
Folder matching is case-insensitive: Steam, steam, and STEAM all match.
Example layout that creates three datasources (Default, Steam, Epic):
/mnt/lancache/
├── cache/
│ ├── 00/, 01/, a1/, ff/ ← Default cache (hash dirs at root)
│ ├── steam/
│ │ └── 00/, 01/, ... ← Outsourced Steam
│ └── epic/
│ └── 00/, 01/, ... ← Outsourced Epic
└── logs/
├── access.log ← Default log
├── steam/
│ └── access.log ← Steam log
└── epic/
└── access.log ← Epic log
For drives in totally separate locations or finer control, declare each datasource explicitly. Manual config wins over auto-discovery if both are set.
environment:
# Main LANCache
- LanCache__DataSources__0__Name=Default
- LanCache__DataSources__0__CachePath=/cache
- LanCache__DataSources__0__LogPath=/logs
- LanCache__DataSources__0__Enabled=true
# Steam on a separate drive
- LanCache__DataSources__1__Name=Steam
- LanCache__DataSources__1__CachePath=/steam-cache
- LanCache__DataSources__1__LogPath=/steam-logs
- LanCache__DataSources__1__Enabled=trueWith matching volume mounts:
volumes:
- /mnt/lancache/cache:/cache:ro
- /mnt/lancache/logs:/logs:ro
- /mnt/steam-drive/cache:/steam-cache:ro
- /mnt/steam-drive/logs:/steam-logs:roLANCache Manager runs fine behind nginx. HTTPS is recommended, and required if you plan to use guest sessions across origins (cross-origin image cookies need Secure).
Serve the UI and API from the same hostname. Cookies stay first-party, CORS is a non-issue.
server {
listen 443 ssl http2;
server_name lancache.example.com;
ssl_certificate /etc/letsencrypt/live/lancache.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/lancache.example.com/privkey.pem;
# Increase if you have large responses
client_max_body_size 50m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
# SignalR (WebSockets)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s; # Must match SignalR timeout (10 min) to prevent nginx killing idle WebSocket connections
}
}
server {
listen 80;
server_name lancache.example.com;
return 301 https://$host$request_uri;
}If the UI and API live on different hostnames:
- Build the UI with
VITE_API_URL=https://api.lancache.example.com. - Keep
SameSite=None; Securecookies (the app already sets this). - Allow credentials in CORS for the UI origin.
server {
listen 443 ssl http2;
server_name api.lancache.example.com;
ssl_certificate /etc/letsencrypt/live/api.lancache.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.lancache.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
# SignalR (WebSockets)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s; # Must match SignalR timeout (10 min) to prevent nginx killing idle WebSocket connections
}
}The app exposes Prometheus metrics on /metrics. Scrape them, build dashboards, alert on cache hit ratio — whatever you need.
| Metric | Description |
|---|---|
lancache_cache_capacity_bytes |
Total storage capacity |
lancache_cache_size_bytes |
Currently used space |
lancache_cache_hit_bytes_total |
Bandwidth saved (cache hits) |
lancache_cache_miss_bytes_total |
New data downloaded |
lancache_active_downloads |
Current active downloads |
lancache_cache_hit_ratio |
Cache effectiveness (0-1) |
lancache_downloads_by_service |
Downloads per service |
lancache_bytes_served_by_service |
Bandwidth per service |
scrape_configs:
- job_name: 'lancache-manager'
static_configs:
- targets: ['lancache-manager:80']
scrape_interval: 30s
metrics_path: /metricsIf you've set Security__RequireAuthForMetrics=true, add bearer auth:
authorization:
type: Bearer
credentials: 'your-api-key-here'# Cache hit rate as percentage
lancache_cache_hit_ratio * 100
# Bandwidth saved in last 24 hours
increase(lancache_cache_hit_bytes_total[24h])
# Cache size in GB
lancache_cache_size_bytes / 1024 / 1024 / 1024
- Check the log path in Management > Settings.
- Confirm your volume mount lines up with
LanCache__LogPath. - Click Process Logs in Management.
- Look at the container logs:
docker logs lancache-manager.
Steam:
- Pull the latest mappings from Management > Depot Mappings.
- Add custom mappings for any private depots you care about.
- Click Reprocess All Logs after adding mappings.
Epic:
- Open Management > Integrations and run Epic game mapping.
- The mapping service queries the Epic API to identify what's in your cache.
- Game names and cover art come down automatically.
cat ./data/security/api_key.txt
# or
docker logs lancache-manager | grep "API Key"To rotate the key, stop the container, delete ./data/security/api_key.txt, and start it again.
Make sure PUID and PGID match the owner of your cache and log files:
ls -n /path/to/cacheThis applies to both Steam and Epic.
- Confirm the Docker socket is mounted.
- Confirm you're authenticated as admin in lancache-manager.
- Look in the container logs for the network diagnostics block (
═══ PREFILL CONTAINER NETWORK DIAGNOSTICS ═══). - No internet inside the prefill container. The container can't reach Steam or Epic. Common fixes:
- Set
Prefill__NetworkMode=bridge(works for most setups). - Confirm your Docker network has outbound internet.
- Check firewall rules for outbound traffic.
- Set
- HTTP 400 errors during download. The container can't resolve CDN domains to your cache. Try one of these:
Prefill__NetworkMode=hostif yourlancache-dnsruns on host networking.Prefill__LancacheDnsIpset to your DNS server IP.- Or, the bulletproof option: set
Prefill__LancacheIpto your cache server's IP. This bypasses DNS entirely for CDN traffic and works with any HTTP cache that routes byHost:header. See the Network setup section for details.
- IPv6 sneaking around DNS. If your network has IPv6, queries can bypass
lancache-dns. The app already disables IPv6 in prefill containers to prevent this. - Epic OAuth never connects. Complete the OAuth flow in the browser window that pops open. The token is stored securely and persists across sessions.
To find the IP of your lancache-dns:
docker inspect lancache-dns | grep IPAddressA few configurations as a starting point:
# Bridge mode (recommended default)
environment:
- Prefill__NetworkMode=bridge# Host networking (when lancache-dns also uses host mode)
environment:
- Prefill__NetworkMode=host# Explicit DNS IP (when auto-detection picks the wrong one)
environment:
- Prefill__LancacheDnsIp=192.168.1.20# Lancache IP injection — works in host or bridge mode
environment:
- Prefill__NetworkMode=host
- Prefill__LancacheIp=192.168.1.10 # your cache server IP — bypasses DNS for CDN traffic
- Prefill__LancacheDnsIp=192.168.1.20 # optional — only used in bridge modeIf you're chasing a path resolution, file system, or Docker socket issue, turn on verbose platform logging:
environment:
- Logging__LogLevel__LancacheManager.Infrastructure.Platform=DebugYou'll get extra detail on:
- Path resolution (container vs host paths)
- File system operations and permission checks
- Docker socket communication and container detection
- Volume mount detection
- Linux/Windows platform differences
To use it: add the variable, restart with docker compose up -d, reproduce the issue, then check docker logs lancache-manager. Remove the variable when you're done — it's noisy.
This is most useful when path auto-detection fails, prefill containers won't spawn, volume mounts look wrong, or you're on an unusual platform.
You'll need .NET 8 SDK, Node.js 20+, and Rust 1.75+.
git clone https://github.com/regix1/lancache-manager.git
cd lancache-manager
# Rust processor
cd rust-processor && cargo build --release
# Web interface
cd ../Web && npm install && npm run dev # http://localhost:3000
# API
cd ../Api/LancacheManager && dotnet run # http://localhost:5000Multi-arch Docker build:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/regix1/lancache-manager:latest \
--push .LANCache Manager supports internationalization (i18n) and welcomes community translations. Every UI string is already externalized — there's nothing to refactor before you can translate.
- Fork the repository on GitHub.
- Open
Web/src/i18n/locales/. - Copy
en.jsonto a file named for your language (e.g.,de.json,fr.json,es.json,pt-BR.json). - Translate the values. Leave the keys alone.
- Submit a pull request.
Web/src/i18n/locales/
├── en.json ← English (reference)
├── de.json ← German (your contribution)
├── fr.json ← French (your contribution)
└── ...
- Don't change JSON keys — only translate the string values.
- Preserve placeholders — keep
{{variable}}intact (e.g.,{{name}}). - Preserve formatting — leave HTML tags like
<strong>alone. - Test locally — run the app and verify your translations render correctly.
// en.json
{
"dashboard": {
"title": "Dashboard",
"recentDownloads": "Recent Downloads",
"totalCache": "Total Cache: {{size}}"
}
}
// de.json
{
"dashboard": {
"title": "Übersicht",
"recentDownloads": "Letzte Downloads",
"totalCache": "Gesamter Cache: {{size}}"
}
}Stuck on something? Open an issue on GitHub, or come find the LANCache community on the LanCache.NET Discord.
If LANCache Manager has been useful to you and you'd like to support development, you can buy me a coffee. Every bit helps keep the project alive.
Released under the MIT License.



