Articles

When image derivatives flood the system

drupal performance engineering

Illustration of an industrial conveyor belt, clogged

It begins as a normal content edit. A Drupal editor, eager to populate a new page, adds 30 images into a paragraph (a gallery nugget in Manta Ray Media case). They hit save. Nothing seems amiss. But then… bedlam on the box.

On a typical Drupal site, images aren’t just displayed. Drupal generates derivative versions to match different view modes, screen sizes and responsive layouts. Each time a node is saved, the system loops through every image and creates all the configured derivatives. For a handful of images, this is no problem. But add dozens at once and things start to go wrong, very wrong.

At 30 images in a single paragraph, the standard image derivative pipeline starts to buckle. Each image triggers a GD2 or Imagick operation in the background, invoking convert for resizing. With dozens of images queued, these operations don’t run in parallel, they run synchronously during node save. The result? The website hangs, the editor stares at a spinning save button, The Wheel of Doom, and PHP timeouts become inevitable. The server struggles through a flood of blocking image processes, turning a simple content update into an agonisingly slow ordeal.

What happens behind the scenes when Drupal saves a node with images

When an editor adds multiple images to a paragraph and saves the node, Drupal executes a carefully ordered sequence of steps. For few images, this is usually imperceptible, but when a paragraph contains dozens of images, the effect becomes immediately apparent.

1. Form rebuild and validation (pre-save)

When the form is submitted, Drupal first rebuilds it to validate the input. During this stage:

2. Entity save

The node is then persisted to the database:

3. Form rebuild and rendering (post-save)

Once the node is saved, Drupal rebuilds the form to display messages, errors or handle redirects. At this stage, rendering begins:

4. Deferred image derivatives

On a standard page request, missing image derivatives are generated on demand. However, when editing a node with a large gallery, the derivatives are generated during the edit form rebuild itself. Because each derivative is processed synchronously, the save request can’t be completed, resulting in delays or timeouts.

Why large galleries can hang the node save

To reiterate, the underlying cause of delays when saving a node with many images lies in how Drupal generates image derivatives. Each image field references one or more image styles, which determine the size, format and effects applied to the final output. When an image derivative does not yet exist, Drupal generates it on the fly.

For small numbers of images, this process is usually very quick. However, when a paragraph contains dozens of images, the load becomes significant. Both GD2 and Imagick rely on the convert binary to perform the actual image transformations. These operations are synchronous. The node save request does not complete until all image processing is finished. During this time, the editor may see a spinning save button or experience a timeout. Server resources, particularly CPU and memory, can be heavily taxed.

This is not a bug in Drupal itself but a consequence of the combination of synchronous processing and the volume of images being handled in a single request. Understanding this behaviour is crucial for planning workflows, configuring timeouts and considering alternative approaches for handling large galleries.

How to prevent large galleries blocking node save

The solution lies in removing image processing from the save request entirely and deferring it until the page is actually rendered for a user.

You can achieve this by:

During hook_node_presave, the paragraph containing the gallery is replaced with a placeholder that contains a single dummy image. By doing this, Drupal never processes the full set of images while rebuilding the edit form or saving the node. This immediately removes the risk of the save request being blocked by convert calls.

2. Storing the real images separately

The actual images and any associated paragraph data are stored in a custom table. Drupal’s entity system and the paragraph itself remain unaware of the full image set at this stage. This separation ensures that the standard rendering and image formatters do not trigger derivative generation during save.

3. Preventing field access during form rebuild

To stop Drupal from attempting to render the real images during edit, hook_preprocess_paragraph checks whether the form context is either entity.node.edit_form or entity.node.add_form and unsets the fields that would normally hold the gallery images. This ensures that rendering logic in templates and preprocess functions do not trigger image processing.

When an editor opens the node again, hook_node_load restores the dummy paragraph to the real gallery by retrieving the original images from the custom table and re-mapping them to the paragraph fields. The editor sees the full gallery as if it was never removed and no derivatives have been generated yet.

5. Deferring derivative generation to first page render

For page views, derivatives are generated dynamically via a custom renderer implementing TrustedCallbackInterface. Images are retrieved from the custom storage and rendered in paginated batches through a dedicated AJAX controller. Twig templates receive lazy-loading instructions so that derivatives are created progressively and only for images that are actually needed.

6. Controlled asynchronous processing

By generating derivatives in small batches on demand, each call to convert is limited, preventing blocking, timeouts and server overload. Editors can save large galleries instantly, while visitors still experience the full gallery rendered progressively.

For example, saving 40 images would take roughly 30 seconds, possibly resulting in a timeout. But using this approach, 40 images are saved in less than a second. No Wheel of Doom :-D

Closing thoughts

This issue is easy to miss until it appears in production. On the surface, saving a node with a large gallery looks like a simple content operation. In reality, Drupal’s rendering pipeline ensures that this is not.

The solution described here does not attempt to optimise image processing or work around timeouts. Instead, it accepts how Drupal behaves and restructures the workflow to ensure that image processing simply never occurs during node save or form rebuild. By temporarily removing images from the entity, preventing access to them during preprocessing and restoring them transparently for editors, the save operation remains fast and predictable regardless of gallery size.

Most importantly, this approach preserves the editorial experience. Editors are unaware that images are being substituted or deferred. Visitors still receive fully rendered galleries, generated progressively and in controlled batches. Image derivatives are created only when they are genuinely required and never in a way that blocks critical user actions.

For sites that rely heavily on large, image-driven content, this pattern provides a reliable way to work within Drupal’s constraints. Rather than forcing Drupal to behave differently, it ensures that expensive work happens at the right time. And never at the expense of usability or stability.

This article was originally published 24 February 2026 at mantaraymedia.co.uk.

↑ Contents