Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Flysystem Cloudinary Laravel Package

carlosocarvalho/flysystem-cloudinary

Flysystem adapter that brings Cloudinary storage to Laravel's filesystem abstraction. Use the familiar Storage facade to upload, retrieve, and manage images and files on Cloudinary, combining cloud-based media delivery with Laravel's simple, driver-based file handling in your PHP app.

Deep Wiki
Context7

Technical Evaluation

Architecture fit. Flysystem is the right seam to evaluate: it gives Laravel a single Storage contract across local, S3, and Cloudinary, which is exactly the abstraction that prevents vendor-specific upload code from spreading through controllers and jobs. The adapter's value proposition is real. However, Cloudinary is not a generic object store, and the adapter is a lossy translation layer between two different data models:

  • Flysystem assumes a flat keyspace with hierarchical prefixes. Cloudinary addresses assets by a three-part key (resource_type ∈ image/video/raw, type ∈ upload/private/authenticated, public_id), where slashes in public_id simulate folders but have no independent existence.
  • Flysystem keys typically include an extension (hero.jpg); Cloudinary stores public_id = hero plus a separate format. Any naive bidirectional mapping between path and public_id is a round-trip hazard for listContents → read/delete.
  • Cloudinary's primary delivery mechanism is transformation-on-URL through a CDN. Flysystem's url() returns a plain string; transformation parameters are outside the contract. Teams expecting Storage::disk('cloudinary')->url($path) to emit resized/optimized variants will be disappointed and will end up writing Cloudinary-specific code anyway — partially defeating the abstraction.
  • directoryExists, visibility, and mimeType semantics are approximations. Cloudinary has no POSIX permissions; "visibility" maps at best to upload type.

Integration feasibility. High. This is a standard custom-driver registration: a service provider calling Storage::extend('cloudinary', ...), a disk entry in config/filesystems.php, credentials from env, and league/flysystem v3 already present in any Laravel 9+ app. No framework patching, no build tooling, no frontend surface. Realistic effort for a competent Laravel engineer: half a day to a working prototype, two to five days to production-grade with tests.

Technical risk. The dominant risk is not code quality — it is adoption and maintenance signal. 22 stars and a single-namespace vendor package means effectively zero community backpressure. The stated last release of 2026-04-04 is future-dated relative to normal package lifecycles and should be verified against Packagist and the actual repo tags before anyone treats it as actively maintained. There is no evidence in the supplied material of a CI matrix, a Flysystem contract test suite (the league/flysystem-adapter-test-utilities harness), or declared Laravel/PHP version bounds. Treat the high numeric score as a heuristic on packaging quality, not as a diligence substitute.

Key questions for the maintainer / spike:

  1. Which cloudinary/cloudinary_php major is required, and which Laravel and PHP versions are in the CI matrix?
  2. Does the adapter pass league/flysystem-adapter-test-utilities? Which contract tests are skipped, and why?
  3. How is public_id ↔ path mapping done for files with extensions, and is it stable across write → listContents → delete?
  4. Does it implement PublicUrlGenerator / TemporaryUrlGenerator, and how are Cloudinary signed/private delivery URLs surfaced?
  5. How are move/copy implemented — native Cloudinary rename or copy+delete?
  6. Does listContents use the Admin API per call? If so, what is the pagination and rate-limit behavior on a 10k-asset prefix?
  7. How is deleteDirectory implemented — delete_resources_by_prefix? Can it be made opt-in/guarded?
  8. Does the adapter surface resource_type/type in listings, or does it hide them (which would make deletion ambiguous)?

Integration Approach

Stack fit. Laravel 9/10/11 with Flysystem 3 and PHP 8.1+ is the target envelope; verify against this package's composer constraints before committing. Assume the Cloudinary PHP SDK is a transitive dependency. This fits cleanly into an app that already uses Storage for user uploads, but it is a media disk, not a general-purpose filesystem — do not point logs, exports, or backups at it.

Migration path. Do not swap the default disk.

  1. Register Cloudinary as an additional disk (cloudinary) and keep local/s3 as the default for non-media.
  2. Introduce a thin internal MediaStore interface wrapping the disk, so the Flysystem adapter is one implementation and the Cloudinary SDK is a fallback for transformation URLs. This caps the blast radius if the package is abandoned.
  3. Dual-write new media to the existing store and Cloudinary for one release cycle; backfill legacy assets with a queued job that records public_id/resource_type/type alongside your existing file record.
  4. Read from Cloudinary behind a feature flag; flip per-surface (thumbnails first, then full downloads).
  5. Decommission the old write path once the backfill is verified and the flag has been at 100% for a full retention window.

Compatibility. Pin the adapter and the Cloudinary SDK to exact versions in composer.json. Add a CI job that runs the Flysystem adapter contract test utilities against a sandbox Cloudinary account, plus a nightly smoke test that writes, lists, and deletes a scratch asset — this catches silent API drift far earlier than production. Verify url() output against your CDN/transformation requirements before designing front-end components around it.

Sequencing.

  1. Spike (1–2 days): prototype disk, contract test run, key-mapping behavior documented.
  2. Decision gate: maintainer responsiveness, license, test coverage, and whether url()/temporaryUrl() meet requirements.
  3. If adopted: add MediaStore wrapper, config, credential handling via secrets manager.
  4. Dual-write + backfill job.
  5. Canary read path, then full cutover, then cleanup.

If the decision gate fails, fall back to the Cloudinary SDK directly behind the same MediaStore interface — the interface costs little and is the real risk mitigation.

Operational Impact

Maintenance. Bus factor of one is the headline. Budget for the possibility that your team becomes the maintainer. Practical mitigations: vendor the adapter into a first-party namespace (MIT/Apache permitting), or keep the MediaStore boundary thin enough that replacing it is a one-sprint job. Track upstream releases but do not assume they will happen.

Support. Cloudinary support covers the Cloudinary API, not this adapter. Package issues have no SLA and likely no triage. Your on-call runbook must therefore be written against the Cloudinary Admin/Upload API and the Cloudinary console, not against the adapter's documentation.

Scaling. The scaling profile differs sharply from S3-style stores:

  • Reads/delivery scale well — assets are served from Cloudinary's CDN, so origin load is low and latency is good.
  • Writes are bounded by Cloudinary's upload API concurrency, not by your app servers. Bulk imports must be queued, throttled, and idempotent.
  • Listings are the weak spot: listContents on a large prefix means paginated Admin API calls, which are rate-limited per account (limits vary by plan). Any UI that "browses" the disk will be slow and rate-limit-prone. Cache listings; prefer your own database as the index of record and treat Cloudinary as the storage layer.
  • Cost is opaque behind the abstraction. Flysystem has no notion of transformations, which are the primary billing dimension on Cloudinary. A code path that calls a transformation URL per request can 10x spend with no change to application code. Instrument transformation URLs and set account-level alerts before launch.

Failure modes to design against.

  • Partial writes: upload succeeds, your DB row does not — orphaned assets that accrue storage cost. Run a periodic reconciliation job keyed on public_id.
  • Key mismatch on delete: deleting with the wrong resource_type/type silently no-ops, leaving assets that appear deleted in the UI but remain billable and publicly reachable. Verify deletes return the expected result, don't fire-and-forget.
  • deleteDirectory blast radius: if implemented via prefix deletion, one malformed prefix can wipe a large swath of assets. Gate it behind an explicit allowlist and never expose it to user-driven paths.
  • Rate limiting (429): retry with jittered backoff; surface as transient, not as a hard failure.
  • Credential rotation: API key/secret rotation requires a deploy or config reload; plan for zero-downtime rotation.
  • Quota exhaustion: Cloudinary plan limits (storage, transformations, bandwidth) fail closed. Add a pre-upload quota check and alert at 80%.
  • CDN staleness after overwrite: re-uploading to the same public_id may serve stale cached content. Know the invalidation path before you promise "update in place."

Ramp-up. Low for application developers — if they know Storage::put/get/delete, they are productive in an hour. Moderate for whoever owns infra: they must learn Cloudinary's resource model, transformation syntax, Admin API quotas, and the billing console, none of which the adapter abstracts. Plan roughly one engineer-week of operational documentation and runbook work, plus the ongoing cost-governance owner. The adapter makes the happy path cheap; the expensive parts of Cloudinary were never inside Flysystem's scope.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle