# Image transforms

Source: https://docs.fortrabbit.com/dev/performance/image-transforms
Published: 2026-10-26
Reviewed: 2026-10-26

> Resizing images in PHP causes many 504 errors and OOM kills. Generate image transforms in a background worker and keep the source images small.


## About image transforms

Most CMS and many apps create resized copies of uploaded images: thumbnails, responsive sizes, WebP or AVIF versions. Each copy is a transform. PHP loads the full source image into memory and resizes it with GD or ImageMagick. A 6000 pixel camera photo takes about nine times the memory of a 2000 pixel one, and one upload can trigger many transforms at once, one per size and format. With files on S3 or another remote storage, PHP also downloads the source and uploads every result.

When that runs in the PHP processes that serve the site, requests run into the timeout and return a [504 error](/dev/http-status-codes/504), or a process runs [out of memory](/dev/performance/out-of-memory). Other visitors wait for a free [PHP process](/dev/performance/php-processes) meanwhile.

## Crunch images in a worker

We recommend a [worker job](/platform/jobs/workers) for image transforms. The worker runs the queue of the app in its own container: it resizes images in the background while the PHP processes keep serving visitors. Image optimizers such as `jpegoptim` or `pngquant` are installed in [jobs](/platform/components/jobs) only, so they run there too.

1. Book the [jobs component](/platform/components/jobs).
2. Set up the queue of the app, so transforms become queue jobs instead of running in the request.
3. Add a worker job with the queue command of the software.
4. Turn off queue processing in web requests, where the software has it.

| Software | Worker command | Path |
| -- | -- | -- |
| Craft CMS | `php craft queue/listen --verbose` | [Craft CMS image transforms](/guides/craft-cms/image-transforms) |
| Laravel | `php artisan queue:work` | [Laravel image conversions](/guides/laravel/queues-scheduler#image-conversions) |
| Statamic | `php artisan queue:work` | [Statamic image presets](/guides/statamic/tuning#image-presets) |
| Kirby | Plugin, untested | [Kirby thumbs](/guides/kirby/tune#thumbs) |
| WordPress | Plugin, untested | [WordPress image sizes](/guides/wordpress/tune#image-sizes) |

## When transforms run

Without a worker, transforms run in a web request:

| Trigger | Runs in |
| -- | -- |
| Upload | The upload request |
| First view of the image URL | That image request |
| Page render | The page request |
| Queue processed by web requests | A page request |

Transforms triggered by a URL can also be triggered by bots, and they fill the [storage](/platform/components/storage#why-storage-grows).

## Keep source images small

A worker still needs memory for every image.

- Scale images down before uploading them. Twice the largest display size is enough for high-density screens.
- Define fewer sizes and formats. Every combination is another file to generate and store. [Page weight](/dev/performance/page-weight) covers the ones worth generating.

---

- [Worker jobs](/platform/jobs/workers)
- [Out of memory](/dev/performance/out-of-memory)
- [504 Gateway Timeout](/dev/http-status-codes/504)
- [Page weight](/dev/performance/page-weight)
