How I Saved a Friend Hundreds in Hosting Costs by Offloading 240GB to DigitalOcean Spaces

Some months ago, a friend came to me asking for advice.

He runs a pretty huge WordPress website. Not just in traffic — but in assets. We’re talking images, PDFs, PSD files, and more, totaling around 240GB.

Originally, everything was hosted on a managed WordPress hosting provider. And as you can imagine, with that much storage and traffic, he had to scale up regularly. Every upgrade meant the monthly bill kept climbing higher and higher.

It was becoming painful.

So I suggested something simple:

Why not offload all static assets to object storage?

Instead of paying premium hosting prices for file storage, we could use something built specifically for it — Amazon S3 or any S3-compatible storage.

We ended up choosing DigitalOcean Spaces.

For just $5/month, you get 250GB of storage. That alone was already a massive win compared to scaling up his managed hosting plan.

Now let’s get into the technical part.


The Tool: S3 Uploads by Human Made

To offload all the assets properly, I used the excellent:

👉 https://github.com/humanmade/S3-Uploads

It’s lightweight, solid, and works beautifully with WP-CLI.

One important requirement:

You must have WP-CLI running on your server.

The setup instructions on the GitHub repo are already very clear, so I’ll just walk through the important parts here.


Step 1: Configure wp-config.php

First, we need to define the Spaces credentials and bucket configuration inside wp-config.php.

define( 'S3_UPLOADS_BUCKET', 'awesomewebsite' );
define( 'S3_UPLOADS_REGION', 'nyc3' );
define( 'S3_UPLOADS_BUCKET_URL', 'https://awesomewebsite.nyc3.digitaloceanspaces.com' );
define( 'S3_UPLOADS_ENDPOINT', 'https://nyc3.digitaloceanspaces.com' );

define( 'S3_UPLOADS_KEY', 'XXX' );
define( 'S3_UPLOADS_SECRET', 'XXX' );

In my case, all assets were placed in the root directory of the Space.

If you prefer to store them inside a subdirectory (for example /foo), you’ll need to adjust the bucket constants like this:

define( 'S3_UPLOADS_BUCKET', 'awesomewebsite/foo' );
define( 'S3_UPLOADS_BUCKET_URL', 'https://awesomewebsite.nyc3.digitaloceanspaces.com/foo' );

Simple enough.


The DigitalOcean Caveat

By default, S3 Uploads is configured for Amazon S3.

DigitalOcean Spaces is S3-compatible, but there’s a small tweak required.

We need to create a must-use plugin (mu-plugin).

Create this file:

wp-content/mu-plugins/s3-offload.php

And paste the following code:

<?php
/**
* Plugin Name: S3 Uploads DigitalOcean Helper
* Plugin URI: https://www.pixeldevs.io
* Description: Helper file to get DigitalOcean Spaces working with S3 Uploads
* Version: 1.0
* Author: PixelDevs
* Author URI: https://www.pixeldevs.io
**/

add_filter( 's3_uploads_s3_client_params', function ( $params ) {
    if ( defined( 'S3_UPLOADS_ENDPOINT' ) ) {
        $params['endpoint'] = S3_UPLOADS_ENDPOINT;
        $params['use_path_style_endpoint'] = true;
        $params['debug'] = false;
    }
    return $params;
}, 5, 1 );

That’s it. This ensures the S3 client talks properly to DigitalOcean Spaces.


Step 2: Activate the Plugin

From your server, run:

wp plugin activate s3-uploads

Now the site is ready to start offloading.


Step 3: Upload Existing Assets

In our case, everything was inside wp-content/uploads

So I ran:

wp s3-uploads upload-directory wp-content/uploads/ uploads

And then… we waited.

Moving hundreds of gigabytes is not instant. It took quite some time to finish. But once it was done, all uploads were living safely inside DigitalOcean Spaces instead of bloating the web server.

And the hosting bill?

Much lower.


Bonus: Cleaning Up the URLs

After everything worked properly, I also rewrote the asset URLs.

Instead of this:

https://awesomewebsite.nyc3.digitaloceanspaces.com/uploads/2025/09/image.jpg

We now serve files via:

https://assets.awesomewebsite.com/2025/09/image.jpg

Much cleaner. More professional. And ready for CDN in front.

But that’s a story for another day 😉

Leave a Reply

Your email address will not be published. Required fields are marked *