What is Just-in-Time Encoding?
Just-in-time encoding creates a requested media rendition on demand instead of precomputing every resolution, codec, or quality variant. The result may then be cached for later requests.
How Just-in-Time Encoding works
A just-in-time system puts the encoder behind the delivery URL. A request names a source and a recipe; a cache is checked for a stored result and the encode runs only on a miss, after which the output is returned and stored for later requests. That cache is a CDN in front of the encoder, a store the service manages itself, or both. An encoder with neither repeats the work on every request, and a stored result is reused only until it expires or is evicted. For images the unit of work is one URL. For video the practical unit is a segment of an adaptive stream rather than the whole file: an HLS player first requests playlists, which the service generates on the fly, and then requests short segments, each of which is encoded when it is first requested. Only the renditions and time ranges that are requested get encoded; players load segments ahead of the playback position, so that can include segments nobody ends up watching. Mux, which calls this just-in-time transcoding, describes streaming frames out of the transcoder before the rest of the file is complete.
Key facts
- 1Is just-in-time packaging the same thing? No. Just-in-time packaging rewraps renditions that already exist into the streaming format a player asks for. AWS Elemental MediaPackage describes it as creating a manifest in a format compatible with the requesting device; its source content comes from an upstream encoder for live video and, for video on demand, from HLS or MP4 files held in Amazon S3. The nginx-vod-module describes on-the-fly repackaging of MP4 files to DASH, HDS, HLS, and MSS. Neither describes encoding to a new resolution or bitrate, so the encoding ladder still has to be produced in advance. Just-in-time encoding defers that step too.
- 2What does the first request cost, and what if many arrive together? The first request for an output waits for the source to be fetched and for the encode; later requests are cache hits for as long as the result stays cached. imgproxy’s documentation notes that CDNs typically delete rarely accessed content, so a rarely requested output can be encoded more than once. Concurrent first requests for the same output can each start an encode unless the cache in front collapses them into one or the encoder shares the in-flight work itself, as the imagor image server does. Collapsing has conditions: Cloudflare collapses per data center, NGINX’s
proxy_cache_lockis off by default, and on Fastly an uncacheable response makes queued requests go to the origin one after another. - 3What happens to cached output when the encoding settings change? Nothing immediately, unless the change alters the cache key. A stored result is found by its key, typically the source, the processing options, and the output format, so a change to a server-side preset, quality default, or watermark that is not reflected in the request leaves old output in place until it expires or is invalidated. imgproxy documents a
cachebusterURL option for exactly this case and notes that its internal cache has no manual invalidation, only expiration set on the storage behind it. Putting a version in the URL when a recipe changes gives the new output a new key straight away. - 4What should happen when an encode fails? The error response needs its own cache policy. If it carries the lifetime of a successful result, a transient failure sticks: one imgproxy user reported a 504 response that was sent with a one-year
Cache-Controlheader and stored by a downstream NGINX cache. If errors are never cached, every request for a broken source repeats the expensive failure. Without explicit caching headers, HTTP lets a cache store and reuse a 404, which is heuristically cacheable, but not a 500, 503, or 504.Cache-Controldirectives change that in either direction:max-ageorpublicmakes such an error storable, andno-storeforbids storing any response.
When Just-in-Time Encoding matters
Just-in-time encoding pays off when you cannot predict which renditions, or which parts of a file, will be requested: large archives and user uploads where most material is rarely watched, or image sizes that depend on layouts you have not built yet. Encodes that nobody requests are never run. For rarely requested material, how long a result stays cached decides how often a repeat request pays for the encode again. Pre-encode instead when every output has to be ready before publication, when someone has to review it first, or when the first request has to be as fast as every later one. A fixed rendition ladder is not by itself a reason to pre-encode: a just-in-time encoder can serve predefined renditions and still skip the segments nobody requests.
How Transloadit helps with Just-in-Time Encoding
Transloadit offers just-in-time encoding through its Smart CDN. For video, the /video/ondemand Robot generates HLS playlists and segments on demand: a player requests the multivariant playlist from a tlcdn.com URL, each segment is encoded when it is first requested, and the Smart CDN caches playlists and segments for later viewers. For images and other files, a Smart CDN URL runs a Template on a cache miss and serves the cached result until it expires.
The /video/ondemand documentation describes HLS output. The /file/serve Robot sets how long results are cached: by default it tells CDNs to keep a result for 24 hours and browsers for 72 hours, and cache_duration changes both. When playlist URLs are signed with sign_urls_for, shorter expirations reduce cache reuse and can increase segment re-generation. The linked pages do not say what happens to results that are already cached when a Template is edited, or what a player receives when a segment encode fails, so test both before relying on them.
Sources
- Mux engineering blog: just-in-time transcoding and what it avoids encoding
- AWS Elemental MediaPackage: concepts and terminology (just-in-time packaging)
- kaltura/nginx-vod-module README: on-the-fly repackaging and caching recommendations
- RFC 8216: HTTP Live Streaming, Section 6.3.3 (clients load segments in advance)
- RFC 9110: HTTP Semantics, Section 15.1 (heuristically cacheable status codes)
- RFC 9111: HTTP Caching (storing and reusing responses, collapsed requests, Cache-Control directives)
- Cloudflare: default cache behavior, including request collapsing
- NGINX ngx_http_proxy_module: proxy_cache_lock and proxy_cache_valid
- Fastly: request collapsing
- cshum/imagor source: concurrent requests for the same result share one in-flight operation
- imgproxy: internal cache key, expiration, and limitations
- imgproxy: processing options, including cachebuster
- imgproxy issue 686: a maintainer on leaving caching to CDNs and caching servers
- imgproxy issue 1677: Cache-Control sent on a failed request