Problem / Motivation
Glaze currently relies on a Cloudinary-managed upload and rendering flow, but we want a path that lets users keep ownership of their media storage without making us responsible for managing their Cloudinary accounts.
Before committing to any implementation, we should evaluate which media platforms best fit Glaze along two primary dimensions:
- image rendering capabilities that match what we use from Cloudinary today
- ability for the user to bring their own storage
Proposed Solution
Create a comparison matrix for candidate media vendors and document how each one scores on the main behaviors we care about:
- render as JPG
- render with crop
- render with custom resolution
- bring your own storage
Comparison Matrix
| Vendor |
Render as JPG |
Render with crop |
Render with custom resolution |
Bring your own storage |
Glaze fit |
Notes |
| Cloudinary |
Yes |
Yes |
Yes |
Low |
Current baseline, but not a good BYO-storage fit |
Strong all-around image delivery and upload support, but it is primarily a vendor-managed media platform. Sources: Upload widget, Upload API |
| ImageKit |
Yes |
Yes |
Yes |
High |
Best overall fit |
Explicit external-storage support for S3, Azure Blob, GCS, and web servers, plus URL-based image transformation. Sources: Image transformation, Connect external storage, AWS S3 integration |
| Uploadcare |
Yes |
Yes |
Yes |
Medium to High |
Good if we want upload workflow + S3 handoff |
Supports resize/crop transformations and can store or copy uploads to customer S3. Sources: Resize, crop, rotation, AWS S3 storage, Copy files to S3 |
| Cloudflare Images + R2 |
Yes |
Yes |
Yes |
High |
Strong fit if we want storage-first + edge delivery |
Supports keeping originals on your own origin or R2 and transforming them at the edge. Sources: Introduction, Overview, Transform images |
| Filestack |
Yes |
Yes |
Yes |
High |
Flexible, but less obviously aligned |
Supports storage in S3, Azure, GCS, and other cloud storage targets, plus URL-based transformation. Sources: Cloud storage, Processing engine, Transformations |
Cloudflare Images + R2 vs ImageKit
The main decision for Glaze is likely between Cloudflare Images + R2 and ImageKit.
1. Operation complexity of the Glaze-managed flow
- Cloudflare Images + R2 fits naturally if Glaze stays inside Cloudflare's ecosystem. The trade-off is that the product has two distinct modes: remote transformations on outside storage, or hosted images in Cloudflare Images. Those modes bill differently, and the implementation path is easiest when we keep the system narrow.
- ImageKit is more centralized as an image platform. It gives us a single URL-endpoint model with external storages, CDN caching, and transformations on top. That is usually simpler to reason about in the app, but it is a separate vendor with its own storage and CDN model.
2. Operational complexity of users bringing their own storage
- Cloudflare Images + R2 is workable, but the BYO story naturally points users toward a Cloudflare-owned setup such as R2 or another origin that Cloudflare can transform. That means more account-level configuration if each potter owns their own storage.
- ImageKit is explicitly built around external storage and URL endpoints. Its docs support multiple origins per endpoint, custom domains, and even Cloudinary migration helpers, which makes it a better fit if user-owned storage is the primary requirement.
3. Difficulty of integrating with a CDN in both cases
- Cloudflare Images + R2 is the easiest path if we already use Cloudflare for the domain and want Cloudflare to stay the delivery layer. This is the native path.
- ImageKit can sit behind Cloudflare, but that is a more advanced custom-CDN setup rather than the default path. ImageKit documents custom CDN integration and custom cache-key logic as an enterprise-level capability, so if we want Cloudflare in front of ImageKit, the integration is more involved.
4. Pitfalls for the future
- Cloudflare Images + R2 may be the least flexible if we later decide we want per-poter storage ownership without becoming responsible for a Cloudflare account model. It also has two different billing shapes: remote transformations versus hosted Images storage/delivery.
- ImageKit is operationally cleaner for BYO storage, but future custom-CDN needs may push us into enterprise features, and some advanced transformations can be treated as expensive/exceptional usage. That makes it important to keep the early design simple and avoid assuming every future capability will stay on the standard plan.
5. Pricing estimates at various use levels
These are rough estimates, not quotes. They assume a fairly normal image app with externally hosted originals and optimized renditions.
| Use level |
Assumptions |
Cloudflare Images + R2 |
ImageKit |
| Small |
1k originals, 1 variant each, low monthly traffic, under 10 GB storage |
About $0 if we stay inside the free R2 tier and the 5,000 free remote transformations/month |
About $0 on the free plan if bandwidth stays under 20 GB and storage stays under 3 GB |
| Medium |
5k originals, 3 variants each, roughly 100k views/month, ~30 GB optimized delivery |
Roughly $5.30 total: about $5.00 for 10k billable remote transformations above the free 5k, plus a small amount of R2 storage |
Roughly $9/month on Lite if we exceed the free 20 GB bandwidth limit |
| Large |
25k originals, 5 variants each, roughly 1M views/month, ~300 GB optimized delivery |
Roughly $61.35 total: about $60.00 for 120k billable remote transformations above the free 5k, plus a small amount of R2 storage |
Roughly $122.75 on Pro if we need 300 GB bandwidth: $89 base plus 75 GB overage at $0.45/GB |
Recommendation
For a Glaze-style BYO-storage story, the strongest candidate appears to be ImageKit.
It gives us:
- image optimization and delivery comparable to Cloudinary
- external storage support without forcing users into a vendor-owned media account
- a cleaner boundary between "Glaze manages the app" and "the user manages their storage"
If we want the most storage-first architecture, Cloudflare Images + R2 is also worth evaluating, especially if we prefer customer-owned buckets and edge delivery over a traditional media-platform model.
Migration
If we choose ImageKit for user-owned storage, the migration path should be additive rather than disruptive. The default Glaze flow can stay on Cloudinary-managed storage and signed uploads, while ImageKit becomes the opt-in path for users who want to own their media storage.
What we need to store on our side:
- a per-user or per-workspace preference flag that says which storage backend the user wants for new uploads, defaulting to Cloudinary
- the ImageKit connection metadata needed to serve and upload assets, at minimum the ImageKit endpoint / URL endpoint identifier and whatever source mapping is required for the chosen storage origin
- any user-visible display label so the user can tell which storage connection is active
- an enabled/disabled status and basic audit timestamps so we can tell whether the connection is active
What the ImageKit BYO-storage flow should look like:
- the user connects their own external storage in ImageKit, such as S3 or another supported origin
- Glaze stores the connection preference and the ImageKit URL endpoint identity, not the underlying storage credentials
- for uploads, Glaze sends the user into the ImageKit upload flow for that endpoint, or asks ImageKit to accept uploads into the user-connected storage path
- for renders, Glaze stores the returned ImageKit file URL / image identifier on the image record and renders through ImageKit URLs for that user-owned backend
- the render layer chooses the backend per image record, so existing Cloudinary-hosted assets continue to work while new ImageKit assets use the ImageKit URL endpoint
A safe migration would likely look like this:
- keep the current Cloudinary flow as the default for existing users and existing uploads
- introduce ImageKit as a separate storage backend or connection type for new opt-in users
- store per-connection metadata so Glaze can render assets from either backend without changing the rest of the piece/state model
- keep image records backward compatible by preserving
url, cloudinary_public_id, and any new ImageKit identifiers side by side while the transition is in progress
- preserve existing Cloudinary cleanup and admin workflows until ImageKit coverage is complete, then add equivalent cleanup and verification for ImageKit-backed assets
- migrate only new uploads first, then optionally provide a background conversion or re-linking path for users who want to move old assets over later
Acceptance Criteria
Out of Scope
- Implementing any vendor integration
- Changing Glaze’s current Cloudinary flow
- Adding new secrets, credentials, or database tables
- Building a BYO-storage onboarding UI
Problem / Motivation
Glaze currently relies on a Cloudinary-managed upload and rendering flow, but we want a path that lets users keep ownership of their media storage without making us responsible for managing their Cloudinary accounts.
Before committing to any implementation, we should evaluate which media platforms best fit Glaze along two primary dimensions:
Proposed Solution
Create a comparison matrix for candidate media vendors and document how each one scores on the main behaviors we care about:
Comparison Matrix
Cloudflare Images + R2 vs ImageKit
The main decision for Glaze is likely between Cloudflare Images + R2 and ImageKit.
1. Operation complexity of the Glaze-managed flow
2. Operational complexity of users bringing their own storage
3. Difficulty of integrating with a CDN in both cases
4. Pitfalls for the future
5. Pricing estimates at various use levels
These are rough estimates, not quotes. They assume a fairly normal image app with externally hosted originals and optimized renditions.
Recommendation
For a Glaze-style BYO-storage story, the strongest candidate appears to be ImageKit.
It gives us:
If we want the most storage-first architecture, Cloudflare Images + R2 is also worth evaluating, especially if we prefer customer-owned buckets and edge delivery over a traditional media-platform model.
Migration
If we choose ImageKit for user-owned storage, the migration path should be additive rather than disruptive. The default Glaze flow can stay on Cloudinary-managed storage and signed uploads, while ImageKit becomes the opt-in path for users who want to own their media storage.
What we need to store on our side:
What the ImageKit BYO-storage flow should look like:
A safe migration would likely look like this:
url,cloudinary_public_id, and any new ImageKit identifiers side by side while the transition is in progressAcceptance Criteria
Out of Scope