2026-01-21 · 3 min read
Adapter Caching for a Self-Hosted Registry
A self-hosted registry must manage downloads efficiently. Caching at multiple layers reduces bandwidth, improves latency, and scales to hundreds of concurrent clients.
Cache hierarchy
Client browser cache
↓
Client-side local cache (~/.mfl/adapters/)
↓
Optional CDN cache
↓
Origin registry server
↓
Persistent storage (local disk or S3-compatible)
Each layer has different TTL and size constraints.
Browser caching with ETags
HTTP caching is the first line of defense. ModelForgeLab already sets:
Cache-Control: public, max-age=3600
ETag: "8f14e45fceea167a5a36dedd4bea2543b6f2c8d9e1a0473c2f9d1e5a7c3b0d21"
A client browser caches the response for 1 hour. On revisit:
curl -H "If-None-Match: 8f14e45f..." http://registry/download/sdxl-watercolor-lora
# Returns 304 Not Modified (no body transfer)
For immutable versioned resources, use aggressive caching:
Cache-Control: public, immutable, max-age=31536000 # 1 year
Example: /download/sdxl-watercolor-lora-1.4.0.safetensors never changes; cache forever.
For "latest" endpoints: /download/sdxl-watercolor-lora?version=latest, do not cache:
Cache-Control: no-cache, must-revalidate
Client-side local cache
ModelForgeLab CLI caches downloads:
mfl pull sdxl-watercolor-lora
# ~/.mfl/adapters/sdxl-watercolor-lora-1.4.0.safetensors (cached)
mfl pull sdxl-watercolor-lora
# Cache hit! No download.
The manifest tracks cached adapters:
{
"adapters": [
{
"slug": "sdxl-watercolor-lora",
"version": "1.4.0",
"sha256": "8f14e45fceea167a5a36dedd4bea2543b6f2c8d9e1a0473c2f9d1e5a7c3b0d21",
"local_path": "/home/user/.mfl/adapters/sdxl-watercolor-lora-1.4.0.safetensors",
"cached_at": "2025-07-08T10:30:00Z"
}
]
}
Verification is built-in: if the local file's sha256 doesn't match the registry, refetch.
CDN strategy (optional)
For public adapters, place a CDN edge in front:
Client → CloudFlare / Bunny / Fastly
→ Origin (registry)
Configuration:
- Cache everything: Cache-Control from origin honored
- Purge on new release: API call to CDN to invalidate old version
- Georouting: Route downloads through nearest edge
Bandwidth savings: 80–90% of repeated downloads served from edge (no origin cost).
For a 100 MB adapter downloaded 10,000 times/month: - No CDN: 1 TB egress @ $0.10/GB = $100 - With CDN: 900 GB origin + 9.1 TB edge = $90 + $20 (CDN fee) ≈ $110 (break-even at small scale)
Range request handling
ModelForgeLab supports HTTP 206 (partial content) and 416 (range unsatisfiable):
curl -H "Range: bytes=0-999999" http://registry/download/sdxl-watercolor-lora
# Returns 206 Partial Content + Content-Range header
Clients use this to resume interrupted downloads:
mfl pull sdxl-watercolor-lora --resume
# Continues from byte N without re-downloading
Bandwidth planning
Estimate monthly egress:
Adapters in registry: 20
Avg size: 150 MB
Active users: 500
Redownload frequency: 1x / month (most cache locally)
Calculation: - Unique downloads: 500 users × 20 adapters × 1 = 10,000 downloads - Total: 10,000 × 150 MB = 1.5 TB/month - Cost (no CDN): 1.5 TB × $0.10/GB = $150/month
With 85% CDN hit rate: - Origin: 1.5 TB × 0.15 = 225 GB = $22.50 - CDN: 1.5 TB × 0.85 = 1.275 TB = ~$40 (varies by provider) - Total: ~$62.50
Versioned vs latest
/download/sdxl-watercolor-lora-1.4.0.safetensors (immutable, cache forever)
/download/sdxl-watercolor-lora?version=latest (varies, revalidate always)
CLI should prefer versioned URLs for reproducibility:
mfl pull sdxl-watercolor-lora --version 1.4.0
# Uses immutable URL, can cache aggressively
Web UI can default to latest but warn users about reproducibility trade-offs.
Gated adapters
Private adapters must not be cached publicly:
# In registry response handler
if adapter.public:
response.headers["Cache-Control"] = "public, max-age=3600"
else:
response.headers["Cache-Control"] = "private, no-cache"
# CDN cannot cache; browser cache respects auth token TTL
Private adapters are served only to authenticated users. Never place behind a public CDN.
Storage backend
Adapters can live on local disk or S3-compatible storage:
# Local disk (fastest, limited capacity)
adapter_path = "/storage/adapters/sdxl-watercolor-lora-1.4.0.safetensors"
# S3 (unlimited, slower, cheaper for archival)
adapter_path = "s3://my-bucket/adapters/sdxl-watercolor-lora-1.4.0.safetensors"
Hybrid: "hot" adapters on local SSD (cache), others on S3.
Practical registry config
# config.py
CACHE_MAX_AGE = 3600 # seconds
IMMUTABLE_CACHE_MAX_AGE = 31536000 # 1 year
STORAGE_BACKEND = "s3" # or "local"
S3_BUCKET = "my-adapters"
CDN_ENABLED = True
CDN_PURGE_WEBHOOK = "https://api.cdn.example/purge"
When publishing a new version:
# Publish v1.4.1
upload_to_storage(adapter_file, "sdxl-watercolor-lora-1.4.1.safetensors")
register_in_catalog({"version": "1.4.1", "sha256": "..."})
# Purge CDN so latest is fresh
if CDN_ENABLED:
purge_cdn(pattern="sdxl-watercolor-lora*")
Bandwidth and caching are not glamorous, but they directly impact user experience. Lazy downloads feel instant with good caching; slow ones destroy trust.