- Added new targets in the Makefile for validation, including `run-validation`, `validate-check`, and `validate-kill`, to streamline the testing process with external tools like SteamPrefill. - Introduced a `setcap` target to manage necessary capabilities for running the server on port 80 without root access. - Updated README.md to include detailed instructions for validating functionality, including quick start guides and troubleshooting tips. - Improved .gitignore to exclude validation artifacts and logs, ensuring a cleaner repository.
SteamCache2
SteamCache2 is a blazing fast download cache for Steam, designed to reduce bandwidth usage and speed up game downloads.
Features
- High-speed caching for Steam downloads
- Tiered storage for getting the most out of your storage media
- Garbage Collected storage for limiting the size of RAM or Disk cache and will not go above what you choose or stop caching unlike others
- Reduces bandwidth usage
- Easy to set up and configure aside from dns stuff to trick Steam into using it
- Supports multiple clients
- NEW: YAML configuration system with automatic config generation
- NEW: Simple Makefile for development workflow
- Cross-platform builds (Linux, macOS, Windows)
Quick Start
First Time Setup
-
Clone and build:
git clone <repository-url> cd steamcache2 make # This will run tests and build the application -
Run the application (it will create a default config):
./steamcache2 # or on Windows: steamcache2.exeThe application will automatically create a
config.yamlfile with default settings and exit, allowing you to customize it. -
Edit the configuration (
config.yaml):listen_address: :80 cache: memory: size: 1GB gc_algorithm: lru disk: size: 10GB path: ./disk gc_algorithm: hybrid upstream: "https://steam.cdn.com" # Set your upstream server -
Run the application again:
make run # or ./steamcache2
Development Workflow
Use make for the majority of common development tasks. The Makefile handles running tests, linting, hygiene checks, building, running the application, and other routine boilerplate work.
Run make help to see the full list of available commands.
This is the preferred approach for day-to-day development. Avoid running raw go test, go run, or golangci-lint commands directly for routine tasks.
Validating Full Functionality with external tools
steamcache2 provides a convenient small-cache configuration and helper targets so you can easily validate behavior using external tools such as SteamPrefill (tpill90/steam-lancache-prefill).
This gives you:
- Real Steam manifest + chunk traffic (no reinventing the wheel)
- Excellent
benchmark setup/benchmark runworkflow with warmup, randomization, and mixed chunk sizes - The ability to validate a just-built binary end-to-end (caching, coalescing, Range support, memory+disk tiers, GC/eviction, metrics, special endpoints, startup validation, etc.)
Quick Start
# 1. Build the binary (this also runs short tests)
make build
# 2. Start a validation-oriented instance (small caches so disk tier + GC get exercised)
# Uses port 80 by default; the script will automatically set the needed
# capability on the binary via sudo setcap if it is missing.
./scripts/validate-with-prefill.sh
# 3. In another terminal (or on another machine), create a workload once if you haven't already,
# then drive it through your local steamcache2.
# Note: when using a non-80 port you may need to give SteamPrefill the full address.
./scripts/validate-with-prefill.sh # (shows the exact commands with the correct port)
When the benchmark finishes, press Ctrl-C in the first terminal to cleanly stop the server.
Simple validation server (recommended for manual testing)
For easy validation with external tools (SteamPrefill, etc.), use:
make run-validation
# or
make validate
This starts steamcache2 on port 80 using a deliberately small memory + disk configuration (good for exercising the disk tier, GC, coalescing, promotions, etc.).
make run-validation (and make validate) will automatically ensure the cap_net_bind_service capability is set on the binary it just built (one sudo prompt the first time after each rebuild). This keeps the server running as your normal user so the disk cache directory stays owned by you.
If you want the capability on the binary for other workflows (e.g. make run, or running the binary directly on port 80), use the explicit target:
make setcap
When the server is running, point your external SteamPrefill (or other load generator) at it:
./SteamPrefill benchmark run ...
When finished, you can get a quick metrics summary with:
make validate-check
This is the recommended simple workflow. No automatic downloading or running of external tools.
Inspecting the Result
After a benchmark run you can ask for a quick report:
make validate-check
# or manually:
curl -s http://localhost/metrics
Look for:
- High cache hit rate after the warmup pass
- Non-zero
coalescedanddiskactivity - Zero unexpected errors
The Validation Config
The script uses docs/examples/validate-config.yaml. It enables both memory and disk tiers at modest sizes (128 MB / 512 MB) with conservative concurrency. Edit or copy it if you need larger caches for bigger workloads.
What Gets Validated
Running a realistic SteamPrefill benchmark workload through a built steamcache2 exercises the complete public surface that matters for production use:
- Steam User-Agent detection and depot/manifest/chunk URL patterns
- Full MISS → cache write → HIT (and HIT-COALESCED) paths
- Range request handling from cached full responses
- Request coalescing under concurrent load
- Memory tier + disk tier interaction (including async disk attach)
- Garbage collection and eviction under pressure
- Metrics and special endpoints (
/,/lancache-heartbeat,/metrics) - Per-client and global rate limiting (with trusted proxy handling)
- Startup configuration validation and upstream behavior
- Clean shutdown hygiene
This is the closest practical equivalent to "run the thing real clients will run and make sure nothing is broken."
Troubleshooting
- Low hit rate on first run: Normal. The first
benchmark runis the warmup that populates the cache. - Want to test real disk I/O (not RAM cache): Make sure your workload size (shown by
benchmark setup) is larger than the total RAM on the machine running steamcache2. - Server won't start or bind on port 80 as non-root:
make run-validationandmake validateautomatically runsetcapon the binary they just built. If it still fails, runmake setcapexplicitly and retry. The server always runs as your normal user (no root) so the disk cache directory ownership stays correct. - SteamPrefill not found: Install it yourself from its GitHub releases. Then use
make validateto start the server with small caches and point SteamPrefill at it manually. - SteamPrefill won't use server as cache properly: SteamPrefill has some bad autodetectiong functions sometimes it works when the server is resolvable from localhost or 127.0.0.1 other times you have to fully override the dns for the proper dns name lancache.steamcontent.com to point to 127.0.0.1 i don't recommend doing it unless your okay with having to undo and redo it depending on if your running the server or not its a pain.
See also the SteamPrefill documentation for benchmark setup and benchmark run options.
Command Line Flags
While most configuration is done via the YAML file, some runtime options are still available as command-line flags:
# Use a custom config file
./steamcache2 --config /path/to/my-config.yaml
# Set logging level
./steamcache2 --log-level debug --log-format json
# Set number of worker threads
./steamcache2 --threads 8
# Show help
./steamcache2 --help
Configuration
SteamCache2 uses a YAML configuration file (config.yaml) for all settings. Here's a complete configuration example:
# Server configuration
listen_address: :80
# P1 hardening (see Security Hardening section)
max_object_size: "0" # 0=unlimited; set e.g. "256MB" for response size DoS protection
trusted_proxies: [] # empty = safe (ignore XFF for rate limit); set CIDRs for trusted proxies
# Cache configuration
cache:
# Memory cache settings
memory:
# Size of memory cache (e.g., "512MB", "1GB", "0" to disable)
size: 1GB
# Garbage collection algorithm
gc_algorithm: lru
# Disk cache settings
disk:
# Size of disk cache (e.g., "10GB", "50GB", "0" to disable)
size: 10GB
# Path to disk cache directory
path: ./disk
# Garbage collection algorithm
gc_algorithm: hybrid
# Upstream server configuration
# The upstream server to proxy requests to
upstream: "https://steam.cdn.com"
Startup Validation
As of P0, steamcache2 performs strict validation on startup (after loading config + CLI overrides, before creating the cache). Invalid configs cause immediate clean failure (no default written, no panic):
- Negative
max_concurrent_requests/max_requests_per_client: "negative concurrency not allowed" - Invalid
gc_algorithm(memory): "invalid memory gc algorithm: badvalue" - Disk enabled (
sizenon-zero/"") but nopath: "disk cache enabled but no path specified" - Invalid memory/disk
sizestrings (via direct New): "invalid memory size: ..." / "invalid disk size: ..." (clean error return, no panic)
Example error on stderr + logs:
Error: Invalid configuration: invalid memory gc algorithm: foo. Please fix the config file and try again.
See config.Validate() and steamcache.New error paths. This ensures the LAN appliance fails fast on misconfig.
Security Hardening (P1)
max_object_size(default "0" = unlimited): set e.g. "256MB" or "512MB" to reject oversized upstream responses with HTTP 413 before buffering/ReadAll. Prevents OOM DoS from large or malicious responses (P1-01). Large legitimate Steam files still served if under limit.trusted_proxies: CIDR list (default empty). When empty (safe default), X-Forwarded-For and client IP spoofing are ignored for rate limiting — always usesr.RemoteAddronly. When set (e.g. your reverse proxy CIDR), uses correct "rightmost untrusted" extraction. Prevents bypass ofmax_requests_per_client(P1-02). Documented for LAN proxy setups only.- These + P0 validation make steamcache2 safe-by-default for LAN exposure.
Migration / Breaking Changes (P1)
New()public signature gained 2 required trailing params (maxObjectSize,trustedProxies). Direct callers (rare; most use config or NewWithOptions) must update.- Recommended: migrate to
NewWithOptions(Options{...})(non-breaking) or rely on YAML config + cmd/root.go. - No behavior change for existing configs (defaults preserve prior semantics).
Large Cache Initialization (async DiskFS population)
disk.New(root, capacity, evictFn)signature changed (now takes evict func fromgc.GetGCAlgorithm, returns error for ctor hygiene). Callers updated internally; direct vfs/disk users must pass the evict (or nil for no startup guard).- DiskFS initialization is now fully asynchronous for large caches (millions of files):
Newreturns immediately without scanning. The firstSize()(and many internal callers) blocks on an internal barrier until bg streaming population + any startup over-cap eviction (using the evictFn) completes. SubsequentSize()calls are instant. - During the "proxy window" (while bg scan runs): disk-only configs (memory.size=0) have TieredCache Create returning
ErrNotFound(no disk writes/caching occurs until attach); mem+disk configs serve from memory tier only. This keepsNewfast and avoids heavy disk I/O/eviction during long scans on slow storage. - The explicit startup guard (reduce size if pre-existing on-disk > cap) runs as the literal last step of bg init, before the barrier opens.
- Add a note for operators: very large disk caches (tens/hundreds GB with millions files) may show extended "memory-only or no-cache" behavior at startup (seconds to minutes depending on storage speed); this is by design for responsiveness.
- Godoc on
disk.NewandDiskFS.Sizeexpanded with the barrier/attach behavior.
Garbage Collection Algorithms
SteamCache2 supports different garbage collection algorithms for memory and disk caches, allowing you to optimize performance for each storage tier:
Available GC Algorithms:
lru(default): Least Recently Used - evicts oldest accessed fileslfu: Least Frequently Used (P1 real impl) - evicts by lowest AccessCount (tiebreak older ATime); uses existing FileInfo countersfifo: First In, First Out - evicts oldest created files (predictable)largest: Size-based - evicts largest files first (maximizes file count)smallest: Size-based - evicts smallest files first (maximizes cache hit rate)hybrid: Recency + frequency hybrid (P1 meaningful) - evicts by lowest time-decayed score (GetTimeDecayedScore combining ATime + AccessCount)
Recommended Algorithms by Cache Type:
For Memory Cache (Fast, Limited Size):
lru- Best overall performance, good balance of speed and hit ratelfu- Excellent for gaming cafes where popular games stay cachedhybrid- Optimal for mixed workloads with varying file sizes
For Disk Cache (Slow, Large Size):
hybrid- Recommended for optimal performance, balances speed and storage efficiencylargest- Good for maximizing number of cached fileslru- Reliable default with good performance
Use Cases:
- Gaming Cafes: Use
lfufor memory,hybridfor disk - LAN Events: Use
lfufor memory,hybridfor disk - Home Use: Use
lrufor memory,hybridfor disk - Testing: Use
fifofor predictable behavior - Large File Storage: Use
largestfor disk to maximize file count
DNS Configuration
Configure your DNS to direct Steam traffic to your SteamCache2 server:
- If you're on Windows and don't want a whole network implementation, see the Windows Hosts File Override section below.
Windows Hosts File Override
-
Open Notepad as Administrator:
- Click on the Start menu, type
Notepad, right-click on Notepad, and selectRun as administrator.
- Click on the Start menu, type
-
Open the Hosts File:
- In Notepad, go to
File>Open. - Navigate to
C:\Windows\System32\drivers\etc. - Select
All Filesfrom the dropdown menu to see the hosts file. - Open the
hostsfile.
- In Notepad, go to
-
Add the Override Entry:
- At the end of the file, add a new line with the IP address of your SteamCache2 server followed by
lancache.steamcontent.com. For example:Replace192.168.1.100 lancache.steamcontent.com192.168.1.100with the actual IP address of your SteamCache2 server.
- At the end of the file, add a new line with the IP address of your SteamCache2 server followed by
-
Save the Hosts File:
- Save the changes by going to
File>Save.
- Save the changes by going to
-
Flush DNS Cache (optional but recommended):
- Open Command Prompt as Administrator.
- Run the following command to flush the DNS cache:
ipconfig /flushdns
-
Restart
- Restart Steam or Restart Your PC
This will direct any requests to lancache.steamcontent.com to your SteamCache2 server.
Building from Source
Prerequisites
- Go 1.19 or later
- Make (optional, but recommended)
Build Commands
# Clone the repository
git clone <repository-url>
cd SteamCache2
# Download dependencies
make deps
# Run tests
make test
# Build for current platform
go build -o steamcache2 .
# Build for specific platforms
GOOS=linux GOARCH=amd64 go build -o steamcache2-linux-amd64 .
GOOS=windows GOARCH=amd64 go build -o steamcache2-windows-amd64.exe .
Development
# Run in development mode with debug logging
make run-debug
# Run all tests and start the application
make
Troubleshooting
Common Issues
-
"Config file not found" on first run
- This is expected! SteamCache2 will automatically create a default
config.yamlfile - Edit the generated config file with your desired settings
- Run the application again
- This is expected! SteamCache2 will automatically create a default
-
Permission denied when creating config
- Make sure you have write permissions in the current directory
- Try running with elevated privileges if necessary
-
Port already in use
- Change the
listen_addressinconfig.yamlto a different port (e.g.,:8080) - Or stop the service using the current port
- Change the
-
High memory usage
- Reduce the memory cache size in
config.yaml - Consider using disk-only caching by setting
memory.size: "0"
- Reduce the memory cache size in
-
Slow disk performance
- Use SSD storage for the disk cache
- Consider using a different GC algorithm like
hybrid - Adjust the disk cache size to match available storage
Getting Help
- Check the logs for detailed error messages
- Run with
--log-level debugfor more verbose output - Ensure your upstream server is accessible
- Verify DNS configuration is working correctly
License
See the LICENSE file for details. But just for clarity this covers all files in this project unless stated in the individual file.
Acknowledgements
- Inspired by Lancache.net