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.