For the past five years, AWS S3 has been my digital storage backbone. Whether I was building client projects, handling enterprise applications, or just storing personal files, S3 was always there—reliable, scalable, and practically indestructible. I've pushed terabytes through it, set up complex bucket policies, and even built entire content management systems on top of it.
But here's what I learned: sometimes "enterprise-grade" is overkill.
When I finally decided to migrate my blog from Medium to a custom setup powered by Strapi, I thought the hardest part would be content migration. Turns out, I was wrong. The real challenge was something I hadn't anticipated: media management hell.
The AWS S3 Reality Check
Let me paint you a picture of my typical S3 workflow before Cloudinary:
Upload images to S3: (straightforward enough)
Set up CloudFront distribution: for global CDN delivery
Configure Lambda@Edge functions: for image resizing
Write custom code: to handle different formats (WebP, AVIF)
Set up additional buckets: for processed images
Monitor costs: across multiple services
Debug CORS issues: (because there are always CORS issues)
For a simple blog with maybe 50-100 images, this felt like using a sledgehammer to crack a nut. Sure, S3 could handle millions of files without breaking a sweat, but I just needed my hero images to load fast and look good on both desktop and mobile.
The breaking point came when I spent an entire Saturday debugging why my Lambda function wasn't properly generating thumbnails for portrait images. I remember sitting there at 2 AM, staring at CloudWatch logs, thinking: "There has to be a better way."
Enter Cloudinary: The Accidental Discovery
I discovered Cloudinary through pure frustration. A developer friend mentioned it casually in our team Slack: "Why don't you just use Cloudinary? It's basically S3 but for people who actually want to get stuff done."
I was skeptical. After years with AWS, switching felt like abandoning a reliable friend for an unknown quantity. But my Saturday debugging sessions were getting old, so I figured I'd give their free tier a shot.
The onboarding experience was... refreshingly simple. Create account, get API keys, done. No IAM policies, no bucket configurations, no region selection analysis. Just: here's your cloud, start uploading.
The "Holy Sh*t" Moments
URL-Based Transformations Changed Everything
The first time I saw Cloudinary's URL transformations in action, it felt like discovering fire. Want to resize an image to 400x300? Just add /w_400,h_300/
to the URL. Need it as WebP? Add /f_webp/
. Want to blur the background and add a sepia filter? /e_blur:300,e_sepia/
.
Here's what blew my mind: I uploaded one high-resolution image (let's say 4000x3000 pixels), and suddenly I had access to unlimited variations:
/w_300,h_200,c_fill/
- Perfect thumbnail/w_800,h_600,q_auto,f_auto/
- Responsive web version/w_150,h_150,c_thumb,g_face/
- Profile picture with face detection/w_1200,c_scale,q_auto:eco/
- High-quality desktop version
No preprocessing. No storage of multiple versions. No Lambda functions. Just URLs.
Smart Optimization That Actually Works
I'm naturally suspicious of "smart" and "automatic" features—they usually mean "works 70% of the time, breaks spectacularly the other 30%." But Cloudinary's automatic optimization genuinely impressed me.
I ran some tests on my blog images:
Original JPEG: : 2.3MB
Cloudinary auto-optimized: : 340KB (85% reduction)
Visual difference: : Honestly? I couldn't tell.
The magic isn't just compression. Cloudinary analyzes the requesting device and browser, then serves the optimal format. Chrome users get WebP, Safari users get optimized JPEGs, and older browsers get whatever they can handle. It's like having a really smart content delivery network that actually understands images.
The Strapi Integration That Just Worked
Remember my S3 setup process? Hours of configuration, environment variables, and prayer. The Cloudinary-Strapi integration:
Install the plugin:
npm install @strapi/provider-upload-cloudinary
Add three lines to your config file
Upload images through the Strapi admin panel
Images automatically go to Cloudinary with zero additional setup
That's it. No CORS configuration, no signed URLs, no access policies. Just upload and go.
Real-World Usage: What I Actually Do With It
Let me get specific about how Cloudinary changed my daily workflow:
Blog Post Featured Images
Before: Upload to S3, manually resize in Photoshop, create multiple versions for different screen sizes, and upload each version separately.
Now: Upload once to Strapi, let Cloudinary handle everything. My blog template automatically generates responsive image tags:
<picture>
<source media="(min-width: 1200px)" srcset="https://res.cloudinary.com/myblog/w_1200,q_auto,f_auto/featured-image.jpg">
<source media="(min-width: 768px)" srcset="https://res.cloudinary.com/myblog/w_800,q_auto,f_auto/featured-image.jpg">
<img src="https://res.cloudinary.com/myblog/w_400,q_auto,f_auto/featured-image.jpg" alt="Featured image">
</picture>
Image Galleries
My travel posts often include 20-30 images. With S3, I'd spend hours optimizing each one. Now I upload the full-resolution versions and let Cloudinary generate thumbnails on-demand:
Gallery thumbnails:
/w_300,h_300,c_fill,q_auto/
Lightbox images:
/w_1400,h_1000,c_limit,q_auto/
Loading placeholders:
/w_50,h_50,e_blur:2000,q_auto/
Video Content
Here's where Cloudinary really shines beyond images. I occasionally include short video clips in my posts. Previously, this meant:
Export multiple formats (MP4, WebM)
Generate thumbnail images manually
Set up complex video delivery through CloudFront
Handle adaptive bitrate streaming if needed
With Cloudinary, I upload one video file and get:
Automatic format optimization
Generated thumbnails at any timestamp
Adaptive streaming without configuration
Video compression that maintains quality
Performance Impact: The Numbers
I'm a data nerd, so I tracked the performance impact of switching to Cloudinary:
Before (S3 + CloudFront)::
Average page load time: 3.2 seconds
Lighthouse Performance Score: 76
Total image weight per page: ~2.1MB
Time spent on media optimization: 3-4 hours per blog post
After (Cloudinary)::
Average page load time: 1.8 seconds
Lighthouse Performance Score: 94
Total image weight per page: ~650KB
Time spent on media optimization: Maybe 5 minutes
The difference isn't just in numbers—my pages feel noticeably snappier. The combination of smart optimization and global CDN delivery creates a genuinely better user experience.
Beyond the Basics: Advanced Features That Surprised Me
AI-Powered Auto-Tagging
Cloudinary automatically tags uploaded images with relevant keywords. It identified "mountain," "sunset," and "hiking" in my travel photos without any input from me. While I don't rely on this for my blog's taxonomy, it's incredibly useful for content discovery.
Background Removal
Need to remove backgrounds from product images or profile photos? Cloudinary does it automatically with /e_background_removal/
. It's not perfect, but it's often good enough to skip Photoshop entirely.
Face Detection and Smart Cropping
The /c_thumb,g_face/
parameter automatically detects faces and crops thumbnails to keep them centered. Perfect for author bios or team pages where you need consistent headshots from various source images.
Advanced Video Features
Auto-generated subtitles: for accessibility
Video thumbnails: at specific timestamps
Animated GIF generation: from video segments
Quality analysis: to identify and fix compression artifacts
Transformation Chaining
You can chain multiple transformations in a single URL:/w_800,h_600,c_fill,g_auto,e_improve,e_sharpen:100,f_auto,q_auto/
This single URL crops to 800x600, auto-focuses on the most interesting area, improves the image quality, adds sharpening, and delivers in the optimal format. It's like having a professional photo editor built into your URLs.
The Developer Experience
Coming from AWS, I was prepared for extensive documentation, complex APIs, and a steep learning curve. Cloudinary surprised me with its developer-friendly approach:
API Design
The REST API is intuitive and well-documented. Upload endpoints that actually make sense:
// Upload with transformation
const result = await cloudinary.uploader.upload("local-image.jpg", {
width: 500,
height: 500,
crop: "fill",
quality: "auto",
format: "auto"
});
SDKs That Don't Suck
I've used AWS SDKs extensively, and while powerful, they often feel like they were designed by committee. Cloudinary's JavaScript SDK is clean, well-typed (if you're using TypeScript), and genuinely pleasant to work with.
Upload Widgets
For client-side uploads, Cloudinary provides embeddable widgets that handle everything from drag-and-drop to progress indicators. Compare this to building your own S3 upload interface with presigned URLs and CORS configurations.
Cost Reality Check
Let's talk money, because this always matters for side projects and small businesses.
My Usage Pattern
Monthly uploads: ~50 images, 5-10 videos
Monthly bandwidth: ~15GB
Storage: ~8GB total
AWS S3 + CloudFront + Lambda@Edge costs::
S3 storage: ~$2/month
CloudFront data transfer: ~$8/month
Lambda@Edge executions: ~$3/month
Total: ~$13/month:
Cloudinary costs::
Free tier covers: 25GB storage, 25GB bandwidth, 25,000 transformations
My usage: Well within free tier limits
Total: $0/month:
For heavier usage, Cloudinary's paid plans start at $89/month, which includes 100GB storage and 100GB bandwidth. That might seem steep compared to AWS, but consider what you're getting:
No Lambda function costs
No CloudFront configuration
No image processing server maintenance
Built-in optimization and CDN
Advanced AI features included
The Hidden Costs of AWS
What AWS pricing doesn't show:
Development time: : Setting up and maintaining image processing pipelines
Debugging time: : Lambda function issues, CORS problems, CloudFront cache invalidation
Monitoring costs: : CloudWatch logs and metrics
Complexity tax: : The mental overhead of managing multiple services
When I factor in the 10-15 hours per month I was spending on media-related infrastructure issues, Cloudinary's pricing starts to look very reasonable.
What Could Be Better
I don't want to sound like a Cloudinary evangelist—no service is perfect, and there are legitimate reasons to stick with AWS:
Vendor Lock-in Concerns
With S3, your images are just files in a bucket. Easy to migrate, easy to backup, easy to access directly. With Cloudinary, you're more tied to their platform. While they provide APIs for bulk download and migration tools, it's not as straightforward as aws s3 sync
.
Advanced Control
AWS gives you granular control over every aspect of your infrastructure. Want custom caching rules? Custom image processing algorithms? Specific geographic restrictions? AWS makes this possible (though not easy).
Cloudinary is more opinionated. Their algorithms are generally excellent, but if you need something very specific, you might be out of luck.
Enterprise Integration
If you're already deep in the AWS ecosystem with VPCs, custom IAM policies, and complex compliance requirements, Cloudinary might introduce unwanted complexity rather than reducing it.
Pricing at Scale
For very high-traffic applications, AWS's pay-for-what-you-use model can be more economical than Cloudinary's tier-based pricing. Though at that scale, you probably have a dedicated DevOps team anyway.
The Decision Framework
So when does it make sense to choose Cloudinary over S3? Based on my experience:
Choose Cloudinary if::
You're building content-heavy websites (blogs, portfolios, e-commerce)
You want to focus on content, not infrastructure
Your team is small and time is valuable
You need advanced image/video processing without the complexity
You're starting fresh (no existing S3 infrastructure)
Stick with AWS if::
You're already heavily invested in the AWS ecosystem
You need very specific image processing requirements
You have enterprise compliance requirements
Your traffic patterns are highly unpredictable
You have dedicated DevOps resources
Six Months Later: No Regrets
It's been six months since I made the switch, and I haven't looked back. My Saturday debugging sessions are a thing of the past. When I want to add an image to a blog post, I just upload it and move on.
The time savings have been dramatic. What used to be a half-day project (writing a post + handling media) is now genuinely a 2-hour task. I spend my weekends writing instead of wrestling with Lambda functions.
More importantly, my blog's performance improved across the board. Faster loading times mean better user experience, which means people actually read what I write. Isn't that the whole point?
Give It a Shot
If you're dealing with image-heavy projects and finding AWS more frustrating than helpful, try Cloudinary's free tier. Upload some images, play with the URL transformations, and see how it feels.
You might find, like I did, that sometimes the best solution isn't the most powerful one—it's the one that gets out of your way and lets you focus on what actually matters.
Because at the end of the day, nobody visits your blog to admire your Lambda functions. They come for your content. Make sure that's where you're spending your energy.