Skip to content

regix1/lancache-manager

Repository files navigation

LANCache Manager

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

Table of Contents


Screenshots

Dashboard

Dashboard Overview Dashboard Stats

Stats at a glance with draggable cards, service analytics, and top clients

Downloads

Normal View Downloads - Normal View Downloads - Game Detail

Compact View Downloads - Compact View

Retro View Downloads - Retro View

Three view modes to browse your cached games

Clients

Clients

Monitor which devices are using your cache

Events

Events Calendar

Calendar view of download activity and LAN events

Prefill

Prefill Home

Pick between Steam and Epic to start a prefill session

Prefill Session

Game selection, download settings, and real-time activity log

Management

Management - Settings

Authentication, demo mode, and display preferences

Management - Logs & Cache Management - Cache Operations

Log processing, corruption detection, and game cache detection

Management - Themes Management - Theme Editor

Browse installed themes or create your own with the theme editor

Management - Client Nicknames

Assign friendly names to client IPs and exclude clients from stats


Quick Start

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:latest

Then:

  1. 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.

  2. Open http://localhost:8080.

  3. Head to Management, paste your API key, and click Process Logs to import your existing cache history.


Docker Compose

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=/cache

Drop :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

Configuration

Volumes

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

Required Settings

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.

PostgreSQL

LANCache Manager stores everything in PostgreSQL. There are two ways to set the password:

  • Set POSTGRES_PASSWORD in 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"

Security

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.

Access Levels

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.

File Browser (DeveLanCacheUI Import)

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,/mnt

If you don't use the DeveLanCacheUI importer, leave it empty. The warning is informational — nothing else in the app cares.

Prefill

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.

Paths and Datasources

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.

Nginx Log Rotation

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.

API and Advanced

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 (Steam & Epic)

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.

Running a prefill

The flow is the same for both services:

  1. Open the Prefill tab and pick Steam or Epic Games
  2. Sign in (Steam Guard for Steam, OAuth for Epic)
  3. Pick games from your library
  4. 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.

Importing Steam App IDs

Have a list of App IDs from steam-lancache-prefill or somewhere else? Skip the library browser:

  1. Click Select Apps
  2. Click Import App IDs
  3. Paste your IDs in any of these formats:
    • Comma-separated: 730, 570, 440
    • JSON array: [730, 570, 440]
    • One per line
  4. 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.

Requirements

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

Network setup

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.

Quick recommendation

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.

Settings

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.

Examples

Most reliableLancacheIp makes CDN routing DNS-independent:

environment:
  - Prefill__NetworkMode=host
  - Prefill__LancacheIp=192.168.1.10

Bridge 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 server

Bridge mode, stock lancache-dns, no IP override (legacy DNS-driven path):

environment:
  - Prefill__NetworkMode=bridge
  - Prefill__LancacheDnsIp=192.168.1.20

Tip

Prefill container has no internet? Try Prefill__NetworkMode=bridge. Some Docker setups block outbound traffic in host mode.

Network diagnostics

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.

How routing works (advanced)

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
Loading

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.


Custom Themes

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"

Multiple Datasources

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.

Auto-discovery (recommended)

Point the app at the parent directories and let it scan:

environment:
  - LanCache__LogPath=/logs
  - LanCache__CachePath=/cache
  - LanCache__AutoDiscoverDatasources=true

Two things get detected:

  1. Root-level datasource — if /logs/access.log exists and /cache contains LANCache hash directories (00/, 01/, etc.), it creates a "Default" datasource.
  2. Subdirectory datasources — for each folder that exists in both /cache and /logs, it creates a named datasource (e.g., /cache/steam + /logs/steam becomes "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

Manual configuration

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=true

With 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:ro

Reverse Proxy (Nginx)

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

Single origin (recommended)

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;
}

Separate API origin (only if you must)

If the UI and API live on different hostnames:

  • Build the UI with VITE_API_URL=https://api.lancache.example.com.
  • Keep SameSite=None; Secure cookies (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
  }
}

Monitoring (Grafana & Prometheus)

The app exposes Prometheus metrics on /metrics. Scrape them, build dashboards, alert on cache hit ratio — whatever you need.

Available metrics

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

Prometheus config

scrape_configs:
  - job_name: 'lancache-manager'
    static_configs:
      - targets: ['lancache-manager:80']
    scrape_interval: 30s
    metrics_path: /metrics

If you've set Security__RequireAuthForMetrics=true, add bearer auth:

    authorization:
      type: Bearer
      credentials: 'your-api-key-here'

Example queries

# 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

Troubleshooting

Logs aren't processing

  1. Check the log path in Management > Settings.
  2. Confirm your volume mount lines up with LanCache__LogPath.
  3. Click Process Logs in Management.
  4. Look at the container logs: docker logs lancache-manager.

Games aren't being identified

Steam:

  1. Pull the latest mappings from Management > Depot Mappings.
  2. Add custom mappings for any private depots you care about.
  3. Click Reprocess All Logs after adding mappings.

Epic:

  1. Open Management > Integrations and run Epic game mapping.
  2. The mapping service queries the Epic API to identify what's in your cache.
  3. Game names and cover art come down automatically.

Lost API key

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.

Permission issues

Make sure PUID and PGID match the owner of your cache and log files:

ls -n /path/to/cache

Prefill won't run

This applies to both Steam and Epic.

  1. Confirm the Docker socket is mounted.
  2. Confirm you're authenticated as admin in lancache-manager.
  3. Look in the container logs for the network diagnostics block (═══ PREFILL CONTAINER NETWORK DIAGNOSTICS ═══).
  4. 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.
  5. HTTP 400 errors during download. The container can't resolve CDN domains to your cache. Try one of these:
    • Prefill__NetworkMode=host if your lancache-dns runs on host networking.
    • Prefill__LancacheDnsIp set to your DNS server IP.
    • Or, the bulletproof option: set Prefill__LancacheIp to your cache server's IP. This bypasses DNS entirely for CDN traffic and works with any HTTP cache that routes by Host: header. See the Network setup section for details.
  6. 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.
  7. 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 IPAddress

A 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 mode

Debug logging

If you're chasing a path resolution, file system, or Docker socket issue, turn on verbose platform logging:

environment:
  - Logging__LogLevel__LancacheManager.Infrastructure.Platform=Debug

You'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.


Building from Source

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:5000

Multi-arch Docker build:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t ghcr.io/regix1/lancache-manager:latest \
  --push .

Contributing Translations

LANCache Manager supports internationalization (i18n) and welcomes community translations. Every UI string is already externalized — there's nothing to refactor before you can translate.

How to contribute

  1. Fork the repository on GitHub.
  2. Open Web/src/i18n/locales/.
  3. Copy en.json to a file named for your language (e.g., de.json, fr.json, es.json, pt-BR.json).
  4. Translate the values. Leave the keys alone.
  5. Submit a pull request.

File layout

Web/src/i18n/locales/
├── en.json          ← English (reference)
├── de.json          ← German (your contribution)
├── fr.json          ← French (your contribution)
└── ...

Guidelines

  • 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.

Example

// en.json
{
  "dashboard": {
    "title": "Dashboard",
    "recentDownloads": "Recent Downloads",
    "totalCache": "Total Cache: {{size}}"
  }
}

// de.json
{
  "dashboard": {
    "title": "Übersicht",
    "recentDownloads": "Letzte Downloads",
    "totalCache": "Gesamter Cache: {{size}}"
  }
}

Support and License

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.

About

LANCache Manager - Self-hosted web dashboard for monitoring Lancache data, Real-time download tracking, bandwidth analytics, cache management, and client monitoring. Docker deployment with Prometheus metrics and Grafana integration.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors