Presets & Caching
Two concerns that sit behind the resize and format parameters: giving a recurring combination a name, and making sure the second request for it costs nothing.
Named Presets
A preset bundles width, height, fit, crop, quality and format under one name. It is operator configuration — a JSON object in an environment variable, not app schema — because the right thumbnail size is a property of the deployment, not of the business domain.
STORAGE_TRANSFORM_PRESETS='{
"thumbnail": { "width": 150, "height": 150, "fit": "cover" },
"preview": { "width": 600, "quality": 80 },
"avatar": { "width": 64, "height": 64, "fit": "cover", "crop": "attention" }
}'
GET /api/buckets/photos/files/{key}?preset=avatar
GET /api/buckets/photos/files/{key}?preset=thumbnail&quality=95
| Situation | Result |
|---|---|
| Known preset | Its fields become the transform. |
| Explicit parameter alongside a preset | The explicit value wins, field by field. |
| Unknown preset name | 400 |
?preset= with none configured |
400, with a message saying so. |
Overrides are per-field, not all-or-nothing: ?preset=thumbnail&quality=95 keeps the preset's 150×150 cover and changes only the quality.
Preset names must be alphanumeric with single hyphens (thumbnail, hero-banner), which keeps them safe to put in a URL. Malformed JSON, a non-object value, or an illegal name fails the server at startup rather than at the first request — a typo in this variable is found on deploy, not by a user.
Caching
A transform runs once per distinct combination. The result is held in a process-local LRU cache keyed by the storage key, the transform parameters, and the negotiated format together.
| Mechanism | Behaviour |
|---|---|
Cache-Control |
public, max-age=31536000, immutable — one year, never revalidated. |
ETag |
Derived from the source file plus the transform parameters. |
If-None-Match |
A match returns 304 Not Modified. |
| Server cache | In-memory LRU, evicting least-recently-used entries when it is full. |
| Invalidation | Deleting a file evicts every transform derived from its key. |
| Variable | Default | Meaning |
|---|---|---|
STORAGE_TRANSFORM_CACHE_MAX_SIZE |
256 |
Cache ceiling, in megabytes. |
An entry larger than the whole cap is served but never retained — it would evict everything else to fit.
The cache lives in the process, not on disk. It is empty after every restart and it is not shared between instances, so a multi-process deployment holds one copy per process and warms each independently. That is fine because the CDN-facing Cache-Control is a year: the browser and any edge cache absorb the repeat traffic, and this cache only has to absorb the first request per process.
The one-year immutable header is safe precisely because keys are content-addressed — a stored key is <uuid>-<filename>, so a replaced file is a new key and a new URL. Nothing is ever served stale under a URL whose content changed, because that situation cannot arise.
Originals Are Never Touched
| Guarantee | Behaviour |
|---|---|
| Source bytes | Byte-identical after any number of transform requests. |
| A request with no parameters | The original, subject only to Accept negotiation. |
?format=origin |
The original bytes verbatim, negotiation bypassed. |
| Clearing the cache | Discards derived variants only; originals stay fully available. |
| Non-image files | Transform parameters answer 400 — nothing is attempted. |
Transforms are derived, disposable, and reproducible: the cache can be dropped at any moment and the next request rebuilds it. Admins can do exactly that with DELETE /api/admin/storage/transform-cache.
Recognised image types are those Sovrium can infer from the key's extension: PNG, JPEG, GIF, WebP, AVIF, SVG and ICO. SVG passes through rather than being rasterised, and is always served as an attachment — see Upload Security.
Related Pages
- Resize & Crop — the parameters a preset bundles.
- Format & Quality — format negotiation and compression.
- File Operations — the endpoint serving cached results.
- Lifecycle & Quotas — the originals a transform reads from.
- Environment Variables: Services — preset and cache configuration.
Last updated July 27, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.