Skip to main content

Command Palette

Search for a command to run...

Why We Ditched the AWS SDK for a 1.5MB Plugin

How SDK bloat in WordPress media offload plugins wastes storage, kills cold starts, and what direct REST API calls fix

Updated
12 min read
Why We Ditched the AWS SDK for a 1.5MB Plugin
WordPress plugin size comparison showing SDK bloat — 87MB vs 1.5MB for cloud media offload

It started as a spare-time infrastructure experiment. I had unused Google Cloud credits sitting idle, so I threw a standard WordPress installation onto Google Cloud Run.

The goal: Can we run WordPress as a horizontally scalable, serverless container?

Everything went well. The Dockerfile built cleanly, the database connected without a hitch, and the site loaded fast. Cloud Run's auto-scaling spun up instances as traffic spiked. But the moment I tried to upload an image to a blog post, the entire setup collapsed.

That single broken upload sent me down a rabbit hole. I discovered just how bloated modern WordPress media offload plugins have become. Some ship weighing almost 90 megabytes.

This is the story of how running serverless WordPress exposed an absurd reality about SDKs, deployment sizes, and hidden performance taxes — and how it led us to engineer a 1.5MB alternative.


The Stateless Problem: Where Do Uploads Go?

If you've tried running a CMS in a stateless environment — Google Cloud Run, Vercel, AWS Fargate, Kubernetes — the biggest headache is always the same: where do the uploads go?

To auto-scale compute instances based on CPU or HTTP traffic, your infrastructure must be strictly stateless. Containers are ephemeral. Created at any moment, destroyed at any moment.

User uploads an image to Container A? Container B has no idea that file exists. Next request routes to B, and that image returns a 404. Spin up a new instance, and images break everywhere.

The fix is straightforward: object storage.

Every file uploaded to WordPress needs to bypass the local ephemeral disk and go straight to Amazon S3, Cloudflare R2, Google Cloud Storage, or DigitalOcean Spaces.

I headed to the WordPress plugin repository assuming I'd find a lightweight offload plugin in five minutes.

I was wrong.


How AWS SDK Bloat Infected WordPress Plugins

My first instinct was to grab a plugin built on the official Google Cloud PHP SDK. Official SDKs are vendor-maintained, well-documented, battle-tested. I downloaded one of the most popular offload plugins, activated it, and checked the installed size.

Over 87 megabytes.

For perspective: the entire WordPress core — the engine powering 43% of the internet — is roughly 73 megabytes. A single plugin whose only job was to push a JPEG to a cloud bucket was larger than the CMS hosting it.

Why? Two words: SDK bloat.

When plugin developers take the path of least resistance, they bundle the entire official SDK. The AWS SDK for PHP (aws-sdk-php) is a brilliant piece of software, but it's monolithic — designed to handle every single AWS service. It ships with 15,000+ files covering everything from EC2 management to Lambda invocations to Quantum Ledger databases.

You need S3 uploads. You get the kitchen sink.

The same applies to the Google Cloud PHP Client. These SDKs are fantastic for full-stack cloud applications. They're wildly overkill for a plugin that calls PUT /object on a bucket.


Why Plugin Size Matters in Serverless Deployments

On a traditional $10/month VPS with 50GB of NVMe storage, dropping an 87MB plugin into wp-content/plugins is barely a blip. It slows down WP-Admin slightly when parsing the plugin list — annoying but manageable.

In serverless and containerized environments, deployment size dictates everything.

Vercel's 250MB Hard Limit

I also tried running WordPress on Vercel (see my vercel-wordpress repo for the proof of concept).

Vercel serverless functions have a 250MB size limit for the entire deployed function. Here's the math on a modern e-commerce stack:

  • WordPress Core: ~73MB

  • WooCommerce: ~51MB

  • Subtotal: 124MB

Already half your budget. Add an 87MB offload plugin and you're at 211MB — leaving just 39MB for your theme, caching, security, SEO, and everything else. One bloated plugin actively blocks you from building a complete application. Compare that to a lightweight WordPress S3 offload plugin at 1.5MB — barely a rounding error.

Cold Starts Get Worse

In Cloud Run or AWS Lambda, cold starts are the enemy. When a new container spins up during a traffic spike, the runtime loads the entire codebase into memory.

Loading 150MB takes measurably longer than loading 80MB. Every unused SDK file adds cold-start latency for exactly the users you're trying to serve during peak traffic.

CI/CD Pipeline Friction

Bloated vendor folders also slow everyday development. They inflate Git repositories, drag out Docker builds, and waste CI/CD time copying thousands of unused API definitions on every deployment.

We wanted a WordPress cloud storage plugin that could offload media without tanking container performance. Since we couldn't find one that was actually lean, we built it ourselves.


Our 1.5MB Architecture: Direct REST Instead of SDK

We boiled the problem down to its essence. What does a WordPress media offload plugin actually need to do?

Strip away the marketing and there are four core operations:

  1. Authenticate request credentials

  2. PUT an object (upload)

  3. DELETE an object (remove)

  4. Generate a signed URL (for private files/downloads)

You don't need a 15,000-file SDK for four API calls. You need authenticated HTTP requests.

Guzzle + Custom Auth Wrappers

Instead of importing massive SDK clients, we used Guzzle for HTTP and wrote our own authentication layer:

// Direct REST call — no SDK overhead
$client = new \GuzzleHttp\Client();
\(response = \)client->request('PUT', $cloud_provider_endpoint_url, [
    'headers' => $authentication_headers,
    'body'    => $file_stream_data
]);

Simple in concept. Hard in practice.

Making raw REST calls to AWS means manually implementing Signature Version 4 (SigV4) — constructing canonical requests, hashing payloads with HMAC-SHA256, building signing strings with precise timestamp headers. It's unforgiving crypto code. One wrong byte in the canonical request and you get a SignatureDoesNotMatch error with zero useful context.

This is exactly why competitors don't bother. Bundling aws-sdk-php and calling $s3Client->putObject() is the easy path. But that convenience costs your users 85MB of dead weight.

We wrote custom, lightweight SigV4 wrappers and direct REST integrations for each provider. Painful to build, but the result speaks for itself.

The Numbers

That engineering effort became CloudSync Master Pro.

Installed plugin size: 1.5MB.

That's roughly 58x smaller than the 87MB alternative. The codebase is 95% strict-typed PHP with zero measurable impact on admin load times or memory footprint.

Because we built a generalized pattern for authenticated REST calls, extending to new providers was straightforward. CloudSync Master supports 10 cloud providersAmazon S3, Cloudflare R2, DigitalOcean Spaces, Backblaze B2, Wasabi, and more — all inside that same 1.5MB footprint. No extra SDKs required.


Background Queues: Fixing WordPress's Sync Bottleneck

The serverless size problem was solved. But testing with realistic loads exposed another bottleneck: synchronous execution blocking.

Upload a single image and WordPress immediately generates 5-10 thumbnail variants depending on your theme. A typical offload plugin hooks into that process and pushes every generated file to the cloud over HTTP — all while the browser waits.

The main PHP thread stays blocked the entire time. The dashboard hangs. Try bulk-offloading 10,000 legacy images and PHP hits max_execution_time, throwing a 504 Gateway Timeout.

Async Queue Architecture

CloudSync Master decouples the upload entirely. Instead of blocking, it queues the job. WordPress's background task system (WP-Cron) validates, compresses, and pushes files asynchronously.

Drop 500 images into your media library, close the browser, grab coffee. The queue pushes everything to your bucket without touching the admin interface.

Real-Time Visibility

Async "black boxes" frustrate people. If a background transfer fails, you need to know what failed and why.

We built a real-time queue dashboard directly into the plugin. Failed transfers show the exact error — network blip, provider timeout, permission issue — with a per-item retry button. No restarting the entire batch.

For power users, there's a concurrency slider:

  • Default: 5 simultaneous uploads (safe for shared hosting)

  • Max: 20 parallel uploads (for capable VPS/container environments migrating large libraries)

Cheap shared hosts stay stable. Beefy infrastructure pushes files as fast as bandwidth allows.


Killing the Configuration Nightmare

With performance solved, one friction point remained: the initial setup.

Most WordPress offload plugins need you to log into the AWS IAM console or Google Cloud Console, create a service account, write bucket policies in JSON, generate access keys, download credential files, and paste long strings into WordPress.

That's 15-20 minutes minimum. Miss a bracket in your IAM policy JSON? "Access Denied" with no useful error message.

Our goal: connecting to Cloudflare R2 or Google Cloud Storage should take 10 seconds, not 15 minutes.

Cloudflare R2: Guided Token Setup

Cloudflare doesn't offer a true OAuth flow yet, but we got close. Click a button in WordPress, the plugin opens Cloudflare's token creation page with the correct API permissions pre-filled. Click "Create Token", paste it back, and your buckets auto-populate in a dropdown. The complex token plumbing is invisible.

Google Cloud: Full OAuth

We registered an official Google OAuth application and integrated it into the Pro tier. Click "Sign in with Google", authorize via the standard popup, and your buckets appear instantly.

No service accounts. No navigating the Google Cloud IAM console. No downloading JSON key files. No copy-pasting policies.

We still support manual access keys for enterprise deployments that demand granular IAM control. But 90% of users pick the quick flow and move on.


WooCommerce Edge Case: Signed URL Routing

One bug worth sharing because it highlights an important architectural lesson about cloud storage object visibility.

For a standard blog, your bucket is public — anyone can access image URLs. But for private digital products (eBook PDFs, premium ZIPs, video courses), you need signed URLs with expiration timestamps.

We integrated CloudSync Master's signed URL feature with WooCommerce digital downloads. Customers purchased perfectly, but clicking the secure download link returned a 403 Forbidden XML error.

Root Cause: Mixed Bucket Domains

Most professional sites map a custom CDN domain to their public bucket (e.g., cdn.yourstore.com pointing to R2). But signed URLs for private files must route through the provider's native API endpoint — not a public CDN proxy.

Our URL generation was bucket-agnostic. When generating a signed URL for a file in the private WooCommerce bucket, it incorrectly applied the public bucket's CDN domain. The browser hit the public proxy looking for a private file, and failed.

The Fix: Bucket-Aware Domain Routing

We rewired the S3Credentials class to handle routing per-bucket:

  1. File in the public bucket? Apply the CDN custom domain.

  2. File in a private bucket? Bypass the custom domain, fall back to the provider's native API endpoint, and generate a cryptographically signed URL with an expiration timestamp.

A small routing change in the code. The difference between a working e-commerce checkout and furious customers flooding your support inbox.


What We Learned: Stop Shipping SDK Bloat

This started as a pet project to run WordPress on Cloud Run without blowing container size limits. It turned into rethinking how offload plugins should be built.

If you're pushing WordPress into serverless, Kubernetes, or edge deployments, plugin size matters far more than most developers realize. Vendor SDKs from AWS and Google are convenient for plugin authors — but they pass the cost to users as cold-start latency, deployment bloat, memory overhead, and thousands of extra files expanding your attack surface.

Three takeaways:

  1. Skip the SDKs. Direct REST calls via Guzzle with hand-rolled SigV4 auth handle everything a media offload plugin needs — at 1.5MB instead of 87MB.

  2. Queue everything. Async background processing keeps cloud API calls from blocking the WordPress admin thread and prevents timeout failures.

  3. Make setup instant. If connecting a cloud provider takes 15 minutes of IAM documentation, the UX has failed.

Your cloud storage plugin should solve media storage limits and cut hosting costs. It shouldn't become a new source of server bloat.


FAQ

Q: How is 1.5MB possible when the AWS SDK alone is 87MB?

CloudSync Master doesn't use the AWS SDK. It makes direct REST API calls via Guzzle and handles SigV4 authentication with custom wrappers. Four API operations (PUT, DELETE, GET, signed URL generation) don't require 15,000 SDK files.

Q: Does skipping the SDK break compatibility with S3-compatible providers?

No. The S3 REST API is a published standard. Any provider that implements it — Cloudflare R2, Backblaze B2, Wasabi, DigitalOcean Spaces — works with the same authenticated HTTP requests. CloudSync Master supports 10 providers this way.

Q: Will this work on shared hosting, or only serverless?

Both. The 1.5MB footprint and async queue architecture run fine on shared hosting, traditional VPS, and serverless containers. The concurrency slider lets you dial back parallel uploads on limited environments.

Q: What happens if a background upload fails?

Failed transfers appear in the real-time queue dashboard with the exact error (network timeout, permission denied, etc.). You can retry individual items without restarting the entire batch.


If you're tired of monolithic plugins slowing down your deployments, give the lightweight approach a try.

Check out the free version on WordPress.org, browse the GitHub repo, or read the comparison with WP Offload Media to see the full size breakdown.

6 views