Async Worker Setup
generation.strategy: async (the default) dispatches GenerateVariant commands onto a Symfony Messenger transport, generation.transport (default async_images). Nothing generates a variant until a worker actually consumes that transport — see Generation Strategies for how the routing gets wired automatically.
1. Configure the transport
# config/packages/messenger.yaml
framework:
messenger:
transports:
async_images: '%env(MESSENGER_TRANSPORT_DSN)%'Any Messenger-supported transport works — Doctrine (a messenger_messages table), Redis, Amazon SQS, etc. You don't need to add a routing entry yourself: the bundle's ProgressiveImageExtension::prepend() sets framework.messenger.routing[GenerateVariant::class] = <generation.transport> for you whenever generation.strategy: async.
2. Run a worker
php bin/console messenger:consume async_images -vvIn production, run this under a process supervisor (systemd, Supervisor, Kubernetes) so it restarts automatically — Messenger workers exit periodically by design (memory limits, --limit, --time-limit), and your supervisor is what keeps one running continuously.
; /etc/supervisor/conf.d/pgi-worker.conf
[program:pgi-worker]
command=php /path/to/app/bin/console messenger:consume async_images --time-limit=3600
numprocs=2
autostart=true
autorestart=true
user=www-data3. What happens while no worker is running
Nothing breaks. ResolveVariantUrlHandler still dispatches the message and immediately falls back per generation.fallback_while_pending (the original image by default) — pages keep working, just without the resized variant, until a worker picks up the queue. This is also exactly what makes sync/terminate reasonable choices for environments where running a separate worker process isn't practical (small deployments, some serverless platforms) — see Generation Strategies for the trade-offs of each.
4. Retrying failures
A failed generation writes a fail marker (throttling retries for variant_store.fail_marker_ttl seconds, default 300) and rethrows — Messenger's own retry policy applies on top of that. Configure it like any other transport:
framework:
messenger:
transports:
async_images:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
retry_strategy:
max_retries: 3
delay: 5000If you'd rather pre-generate known-important variants than wait for a worker to pick them up, see CLI Commands — progressive-image:variant:warm produces exactly the same on-disk artifacts a worker would, just synchronously and on demand.