Media and images: the upload that never stays on the server.
Where an upload goes when the container cannot keep it, which storage mode to choose, how an image CDN turns one file into every size, and the day a bucket setting rewrote every image URL on a site.
Five stops, and the server is only one of them.
A disposable container cannot be the only durable home for an upload. A file on one replica is not automatically available on another. On udx.io, the file passes through the server into object storage. Follow its path and the failure to watch for at each stop.
The editor sends a file to WordPress. The normal media upload is a request to the application; chunking depends on the uploader.
The smallest limit across the proxy, PHP and WordPress wins. Allow for request overhead as well as the file itself.
In this site's Ephemeral mode, WordPress generates its registered sizes, uploads the files to the bucket, then removes the local copies.
PHP still needs a writable temporary directory. A read-only application filesystem does not eliminate upload processing.
The durable objects use year-and-month paths and hashed filenames. Versioning retains older generations when a live object is replaced or deleted.
Uniform bucket-level access disables object ACLs. After 90 consecutive days enabled, it cannot be turned off.
Imgix fetches the source and caches width and format variants. The responses measured here advertise a one-year cache lifetime.
Replacing the source at the same URL can leave old variants cached. Use a new versioned URL or purge the affected variants.
The browser chooses among the image candidates the page provides, based on layout size and display density.
Check srcset and sizes in the rendered page. A phone can still download a large image when those hints are missing or wrong.
Reference: uniform bucket-level access and Object Versioning. These are separate from the image CDN cache policy.
One question: does the server keep a copy?
WP-Stateless, the plugin we maintain for this, has four modes. Two modes retain local media; two remove that dependency. Choose from the storage contract your application and plugins need.
Keeps the file on local disk and copies it to the bucket. Serves from local disk.
- Local copy
- kept
- Served from
- server
- Independent replicas
- local dependency
Keeps the file on local disk and copies it to the bucket. Serves from the bucket.
- Local copy
- kept
- Served from
- bucket
- Independent replicas
- local dependency
Writes the file locally only while sizes are generated, uploads each, deletes the local copies.
- Local copy
- seconds
- Served from
- bucket
- Independent replicas
- supported
Stores media in the bucket without maintaining a local uploads copy. PHP upload handling still uses temporary storage.
- Local copy
- none
- Served from
- bucket
- Independent replicas
- supported
Mode names are the plugin's. The question underneath them applies to any storage plugin on any platform.
One file, every size, measured.
One image from udx.io, measured on October 12, 2026 (UTC). Each row links to the same source or a specific Imgix variant. Sizes are decimal KB; content negotiation can change the format a browser receives.
The 1600-pixel PNG is slightly larger than the original. Resizing alone is not a guarantee of fewer bytes. At 800 pixels, automatic format and compression reduced this response by 57%. The original advertised ten hours of freshness; these CDN variants advertised one year. Both still need a versioned URL or invalidation if the source changes.
The month that moved, and two more.
Three media incidents from the operating record. The first one took six months to understand, because nothing in the application had changed.
The month that moved
Every image URL on a site changed to the current month on the first of the month. The files were fine. The paths were wrong.
Reports came from unrelated sites with nothing in common but the plugin. Reproductions failed because the trigger was a bucket setting, not a plugin setting.
The buckets had switched to uniform bucket-level access. A per-object permission write failed, metadata was not saved, and the plugin regenerated paths using the current month. Uniform access becomes irreversible after 90 consecutive days enabled.
The fix skips the object ACL write for uniform buckets. The lesson: verify plugin compatibility before enabling uniform access, and resolve problems within its 90-day reversal window.
Incident 06 / MediaThe edge forgot the image lifetime+
An image service pod killed for memory once a day
CauseThe CDN honored a 60-second cache lifetime instead of the application's four hours, so the origin re-optimized 3,160 images an hour for 303 distinct variants
Evidence and correctionCDN hit ratio alongside the container memory graph. Fixed with an origin-honoring cache policy and a memory arena cap
View the operating record ↗Incident 07 / MediaOne aborted request wedged the image+
Images failing for 65 percent of visitors over thirty days, with a p95 response time of 30 seconds
CauseAn upstream image optimizer bug: one aborted request on a cold cache key wedged that key for every later visitor, and a year of cached HTML kept asking for an image width the site no longer generated
Evidence and correctionThirty days of CDN standard logs, 2,032 unique clients, status 000 for viewer disconnects. Nothing in application monitoring showed it
View the operating record ↗Also in the record, from pull requests and the plugin issue tracker
You are reading the example.
This handbook is served by the system it describes. Here is the media path behind the page you are reading, checked on October 12, 2026 (UTC).
Ephemeral.
The container keeps an upload only while sizes are generated. The inspected production pod held no retained media files in its uploads directory.
One bucket, versioned.
863 media items, 1.4 GB with every generated size, under year-and-month paths with hashed filenames. Object versioning since 2019; a seven-day soft delete since 2024. Attachment deletion removes the live remote object; versioning and soft delete have distinct recovery rules.
An image CDN in front.
Pages reference the CDN hostname with a width in the query string. The measured variants advertised a one-year cache lifetime. The source advertised ten hours and revalidation.
Generate nothing at upload.
Dynamic image support is off here, so WordPress still produces its standard sizes and uploads each one. Dynamic delivery could remove redundant generated files. First we need to verify the theme, editing flows and plugin compatibility; that change has not been made.
Seven decisions, in order.
Seven decisions to make before media becomes a migration problem.
Choose uniform bucket-level access or object ACLs deliberately. Uniform access becomes irreversible after 90 consecutive days enabled, not 90 days after bucket creation.
Ephemeral suits disposable writable containers. Stateless avoids a local uploads copy, but still needs temporary upload handling. Test the plugins that read or modify media files.
Browser uploads need one even when nothing is kept. Put it on the fastest disposable disk you have.
Route them straight from the bucket. An image optimizer charges for every byte it touches.
A short outer cache lifetime increases requests to the next layer. It does not necessarily regenerate an image: the optimizer may serve its own cached result. Measure each layer.
Cached pages may continue requesting older widths or paths. Preserve those variants or redirect them safely; version new URLs when the underlying image changes.
A stable custom media hostname decouples content from a provider. A later move still needs matching paths, copied objects, TLS and a cache cutover; DNS alone does not move the files.
Asked when the images stop loading.
Should WordPress uploads live on the web server at all?
They can, on a deliberately managed persistent filesystem. For disposable containers and independent replicas, object storage removes the need to share a local uploads directory. Keep attachment metadata in the database and verify how each plugin reads, writes and deletes the remote files.
Which WP-Stateless mode should I use?
Ephemeral for containers with a writable disposable filesystem. Stateless avoids keeping an uploads copy, but PHP still needs temporary upload storage and some plugins need compatibility support. Backup and CDN retain local files. The plugin overview describes the four modes.
Why did all my image URLs change?
Usually because a file's stored path was regenerated. In the incident above, a bucket access-mode change made a permission write fail, the metadata never saved, and the plugin rebuilt the path with the current month on every request. Check the bucket's access mode and the plugin's stored metadata before touching the content.
Does an image CDN replace WordPress thumbnails?
It can, when the theme and media integrations support dynamic delivery. This site still generates standard WordPress sizes and uploads them; Imgix also serves variants from the source. Before disabling local size generation, test editing, srcset, downloads and plugins that reference registered thumbnails. On-demand delivery avoids generating unused variants but still has storage, request and transformation costs.
What about PDFs, video and other files?
Store them in the same bucket, serve them from the bucket or a plain CDN, and keep them away from the image optimizer. Two sites in our record paid for it: one in bandwidth, one in broken downloads because the CDN source did not match the bucket.
Images breaking while every dashboard stays green?
Tell us where uploads live, what serves them and what visitors see. We can scope a media delivery investigation and the engineering work needed to make the path reliable.