Case study: 318 GB of OpenCart photos blocked product uploads

30.10.2026 • 7 views • Category: Website maintenance

Case study: 318 GB of OpenCart photos blocked product uploads

An OpenCart clothing store stopped accepting new product photos. The manager uploaded folders over FTP, and Total Commander stalled at 1% with 550 No such file or directory and “Cannot create remote directory”. FTP wasn’t the problem: product images took up 318 GB of a 394 GB disk, and the server ran out of room to write. We freed up space, removed photos of products no longer in the catalog with our own script, and converted the images to WebP. All of it was done as part of our website maintenance service.

What the client saw

The store’s catalog is filled like this: for every product, the manager creates an FTP folder named after the SKU and uploads the photos into it. One batch is a few dozen products and hundreds of megabytes; a single shot weighed 6.2 MB. At some point uploads started failing:

  • 451 Append/Restart not permitted: the server refused to resume a file after a dropped connection;
  • 550 No such file or directory and “Cannot create remote directory”: the server would not create a folder for a new product.

After five attempts, uploads never got past 1%, and later they stopped starting at all. Nothing had changed on the client’s side, so the first suspects were the FTP client or the network.

Diagnosis: where the space went

Instead of tweaking Total Commander, we checked the server itself. Three commands told the whole story:

  • df -i: inodes (the limit on the number of files) were only 2% used, so the file count wasn’t the issue;
  • df -h: the main partition was 394 GB, with 316 GB used;
  • du -ahx / | sort -rh | head -n 20: almost all of that was the store’s image folder.
FolderSizeContents
image/catalog282 GBoriginal product photos, exactly as uploaded
image/cache36 GBresized copies that OpenCart generates automatically
Total image318 GB81% of the server’s entire disk

Why OpenCart grows like this

It isn’t a bug. These are three OpenCart traits that show up on large catalogs:

  1. The original is stored as is. OpenCart doesn’t downscale uploaded photos. A typical shot in this store is 2235×3365 pixels and 2.3 MB, even though the site never displays it at that size.
  2. Every photo gets a set of copies. OpenCart creates separate thumbnails in image/cache for the product page, listings, cart, comparison and other blocks. Here that came to about 8 sizes per photo.
  3. Deleting a product doesn’t delete its photos. When a product is removed in the admin panel, only the database record goes away; the files stay on disk. The product range had changed over the years, but photos of old collections were still there.

The catalog grows with every delivery, so the disk will fill up sooner or later. The only question is when.

What we did

1. Freed up space to get uploads working again

We started with what could be removed safely: the image/cache thumbnail cache. OpenCart rebuilds it the first time a page is opened. After that, FTP could create folders again and the manager went back to filling the catalog.

2. Wrote a script to find orphaned photos

We passed on ready-made OpenCart cleanup modules. They don’t know about this store’s custom product import and could mistake live photos for junk. So we wrote our own script with simple logic:

  1. collect every product photo path from the database: main images (oc_product) and gallery images (oc_product_image);
  2. walk the originals folder and check each file against that list;
  3. delete any file that isn’t in the database, and delete any product folder left empty.

The core check is just a few lines:

$used = [];
foreach (['product', 'product_image'] as $table) {
    $res = $db->query("SELECT image FROM " . DB_PREFIX . $table . " WHERE image <> ''");
    while ($row = $res->fetch_assoc()) {
        $used[$row['image']] = true;   // catalog/nobrand/038939/2.jpg
    }
}

// path on disk -> path as stored in the database
$relative = str_replace(DIR_IMAGE, '', $fullPath);

if (!isset($used[$relative])) {
    unlink($fullPath);   // dry run: echo $relative;
}

The biggest risk with a script like this is deleting live photos. So this is how we worked:

  • Dry run first. The script deleted nothing. It listed the first 100 candidate files and sample paths from the database. That confirmed the paths on disk and in the database were written the same way (catalog/..., no leading slash). If they had differed by even one character, the script would have treated everything as junk.
  • Product photo folder only. Category, banner and manufacturer images live elsewhere, and the script doesn’t touch them.
  • Run from the console, not the browser. Walking hundreds of gigabytes takes longer than the web server timeout allows. A run cut off halfway would leave it unclear what had already been deleted. Through PHP CLI, the script runs to the end and reports progress every 5,000 files.
  • The script was removed from the server afterwards. Left in the site root, it would let anyone wipe photos just by opening a link.

3. Optimized the photos and converted them to WebP

First we tried lossless compression of the originals with jpegoptim. On the store’s real photos it did almost nothing: 2,274,925 → 2,273,571 bytes, or 0.06%. The studio shots were already well compressed, and there was nothing left to squeeze out without losing quality.

So we wrote our own script that goes through every image, optimizes it and converts it to WebP. There’s an OpenCart catch here: photo paths are stored in the database with a .jpg extension. If you just replace the files on disk with WebP, the images disappear from the site. The format change has to go together with updating the paths in the database.

One more thing: bulk-compressing hundreds of thousands of files puts heavy load on the disk. Run it during low-traffic hours and at reduced priority (nice, ionice) so it doesn’t interfere with cron jobs or product imports.

Result

  • The manager uploads photos over FTP again, with no errors.
  • Photos of products that are no longer in the catalog are gone from the disk.
  • Images are served as WebP, so catalog pages are lighter. The difference is most noticeable on mobile connections.

How to keep your store from getting here

  • Set up a disk usage alert, for example at 80%. An FTP error is already a late symptom.
  • Resize photos before uploading. A 3000+ pixel original stays on disk forever, even though the site needs a much smaller one.
  • Check the size of the image folder once a quarter and clear out photos of deleted products.
  • Before any bulk deletion, make a backup and do a dry run. Deleted photos can’t be recovered without a copy.

We handle tasks like this as part of our website maintenance service, keeping an eye on the server and the site so problems don’t reach your customers. If your OpenCart store has slowed down or your server keeps running out of space, get in touch and we’ll take a look.

Need our services?
Leave a request
By submitting the form, you consent to the processing of personal data. We guarantee that your data
will never be passed on to third parties.
Sending...

Frequently asked questions

Yes. It only holds resized copies, and OpenCart rebuilds them the first time pages are opened. Those first page loads after clearing will be slightly slower.

Standard OpenCart only removes the database records when a product is deleted; the image files stay on disk. They have to be cleaned up separately, by hand or with a script that checks files against the database.

Run df -h (free space) and df -i (file count limit) on the server, and check the FTP user’s disk quota in the hosting panel. Typical symptoms are error 550 when creating a folder and refusals to resume uploads.

Only together with updating the paths in the database: OpenCart stores them with a .jpg extension, and without that change the photos will disappear from the site.

Telegram
Write us on Telegram We reply within 5 min