Glide (local)
Glide processes images locally using GD, Imagick or libvips. Glide 2, 3 and 4 are supported.
composer require league/glidepicasso: transformers: glide: sign_key: '%env(PICASSO_SIGN_KEY)%' cache: '%kernel.project_dir%/var/glide-cache' driver: gd # gd | imagick | vips max_image_size: ~ # optional: max pixel count (width x height) base_url: ~ # optional: e.g. https://img.example.com to point image URLs at a CDN defer_cache_write: false # store a cache miss after the response is sent lock: enabled: false # render a missing variant once (see "Rendering each variant once") public_cache: enabled: false # serve from public dir for better performance prefix: '' # optional: path prepended to cache keys (see "Serving thumbnails from a CDN")Glide URLs are stable: the same image and transformation always produce the same URL and signature. A thumbnail used several times in a page, or across pages, is fetched once and caches cleanly in browsers and CDNs.
A cached variant costs two calls to the cache storage (its size, then its contents), and a conditional request (If-Modified-Since) one: the content type is read from the image’s first bytes. That matters when the cache is an object store, where each call is an HTTP request. On such a storage, responses carry no Last-Modified header, which would cost one more call: signed URLs never change, and cache_control makes them cacheable for a year anyway.
Choosing a driver
Section titled “Choosing a driver”| Driver | Needs |
|---|---|
gd |
The gd extension. The default. |
imagick |
The imagick extension. |
vips |
composer require intervention/image-driver-vips (Glide 3 or later), libvips, and the FFI extension |
libvips is the fastest driver on Linux: in the official Alpine image it renders a whole srcset two to three times faster than GD and Imagick, and AVIF about five times faster (see Performance). Glide hands every driver the whole source file, so libvips cannot shrink large sources while decoding them, and uses about as much memory as the others. The Intervention driver calls it through FFI:
- Install libvips:
apt install libvips42(Debian/Ubuntu),apk add vips vips-heif(Alpine;vips-heifadds AVIF),brew install vips(macOS). - Enable FFI for the web SAPI: the default
ffi.enable=preloadonly allows it on the command line and in preloaded scripts, so setffi.enable=truefor PHP-FPM, FrankenPHP or Apache.
Composer picks the driver version matching your Glide version (1.x for Glide 3, 4.x for Glide 4). A missing package fails at container build and says what to install.
Rendering each variant once
Section titled “Rendering each variant once”Nothing stops several requests from rendering the same missing variant at the same time: a crawler fetching a page’s images, a burst of visitors after a newsletter, or a CDN with many edge locations each asking the origin. Every one of them decodes the source and encodes the variant, so 16 concurrent requests cost 16 renders (and their memory).
With lock, the first request renders the variant while the others wait for it, then serve it from the cache:
framework: lock: '%env(LOCK_DSN)%' # e.g. redis://redis:6379, or flock on a single server
picasso: transformers: glide: lock: enabled: true # factory: lock.factory # the LockFactory framework.lock configures, or your own service # ttl: 30 # seconds a lock outlives a renderer that crashed # wait: 10 # seconds a miss waits for a render in progress- Install the component:
composer require symfony/lock. Use a store shared by every server rendering images (Redis, a database…).flockonly serializes the renders of one server. - Hits take no lock: only a miss locks, keyed by the variant’s cache path.
- With
defer_cache_write, the lock is held until the variant is uploaded: before that, the waiting requests would not find it in the cache. - If the renderer dies, its lock expires after
ttland a waiting request renders the variant itself. Keepttlabove your slowest render (plus its upload withdefer_cache_write). - A request waits at most
waitseconds, then renders the variant itself, without the lock: a render held up (e.g. by an upload to a stalled storage) never keeps the others waiting untilmax_execution_time, a fatal error that also ends a long-running worker (FrankenPHP, RoadRunner). Keepwaitwell undermax_execution_time, leaving room for the render itself. - Waiting requests hold a PHP worker while they wait, idle instead of rendering: they cost a worker slot, not CPU or memory.
Storing the Glide cache on Flysystem
Section titled “Storing the Glide cache on Flysystem”The cache option is polymorphic: pass a local path or the name of a Flysystem bundle storage. The bundle resolves the name at boot via its internal FlysystemRegistry (the same one used by the Vich loader) — if no storage matches, the value is treated as a path. This lets you keep sources and derivatives on different Flysystem instances:
flysystem: storages: sources.storage: adapter: 'aws' options: client: 'aws_s3_client' bucket: 'my-originals' thumbs.storage: adapter: 'aws' options: client: 'aws_s3_client' bucket: 'my-thumbs'picasso: loaders: flysystem: storage: 'sources.storage' # source images transformers: glide: sign_key: '%env(PICASSO_SIGN_KEY)%' cache: 'thumbs.storage' # rendered cache on a different Flysystem driver: gdWith a local path as
cache,public_cachewrites world-readable variants (0644files,0755directories): the web server serves them itself, and may run as another user than PHP (shared hosting), so Flysystem’s default0700directories would make it answer403. With a local Flysystem storage ascache, give it the same permissions (directory_visibility: publicin flysystem-bundle).
